kra-new/docs/audit/parity-identity-access.md

191 lines
34 KiB
Markdown

# GVA Parity Audit: Identity and Access
## Scope and method
- **Reference**: `C:\Users\Yvan\AppData\Local\Temp\gva-parity-02f3783`, commit `02f37833255e0e339c3d69199cb5a468f17de9fc` (the agreed GVA snapshot from 2026-08-06).
- **Audited local surface**: `/base`, `/jwt`, `/user`, `/authority`, `/menu`, `/api`, `/casbin`, `/authorityBtn`, `/department`, `/position`, plus request data-scope construction and policy enforcement.
- **Excluded by request**: code generation/AutoCode, AI/MCP/skills, plugins, and endpoints that are not migrated into this repository.
- **Method**: for every migrated endpoint, compared method/path, binding and defaults, explicit validation, private-route authorization/operation recording, service/repository side effects and transactions, and error/success response envelope.
`OK` means the six dimensions above match the reference for normal operation. The
priority labels follow the consolidated plan: `P1` can change route/management
behavior, `P2` affects deployed clients or failure/rolling-deployment semantics,
and `P3` is a lower-impact observable difference.
## Shared routing and authorization evidence
| Concern | Local evidence | GVA evidence | Assessment |
|---|---|---|---|
| Public/private route split | `internal/server/gin.go:34-61` | `server/initialize/router.go:71-121` | Both expose base routes publicly and run protected routes through JWT, password-expiry guard, Casbin authorization, and data-scope context. |
| Authorization path normalization | `internal/server/middleware/access.go:12-48` | `server/middleware/casbin_rbac.go:12-31` | Both remove `router-prefix` before enforcing `authorityId + path + method`; local reads policies from DB rather than retaining an enforcer cache. |
| Data-scope identity | `internal/server/middleware/access.go:35-47`, `internal/data/authority.go:415-480` | `server/middleware/data_scope.go:12-31`, `server/service/system/data_scope.go:17-101` | Scope, direct/all/subtree/custom department resolution, and request-context injection match. |
| Operation records | `internal/server/middleware/audit.go:137-170` | route-level `middleware.OperationRecord()` in `server/router/system/*.go` | The local exact route allowlist mirrors the reference's recording groups; rejected auth requests are not recorded in either flow. |
## Findings
### P2: security-rate-limit cache namespace differs
`/base/login` and `/base/captcha` count requests under `KRA_SecLimit<ip><route>` locally, but GVA uses `GVA_SecLimit<ip><route>`. With a shared cache during a rolling migration, rate-limit counters do not carry over, so a caller can obtain a fresh counter simply by reaching the other implementation.
- Local: `internal/server/middleware/rate_limit.go:13-36`, especially `:29`.
- GVA: `server/middleware/limit_ip.go:101-104`.
- Recommendation: use `GVA_SecLimit` for the compatible administrative API, or read both prefixes during a documented migration window.
- Regression test: seed `GVA_SecLimit127.0.0.1/base/login` at the configured limit and assert the local `POST /base/login` returns code `7` without invoking the handler.
### P2: version response header name differs on every endpoint
The local global access logger writes `X-Kra-Version`; GVA writes `X-Gva-Version`. This is an externally observable HTTP contract difference for public and protected responses, including failures generated before handlers run.
- Local: `internal/server/middleware/access_log.go:20-36`, especially `:35`.
- GVA: `server/middleware/access_log.go:69-73`, especially `:71`.
- Recommendation: emit `X-Gva-Version` (optionally retain `X-Kra-Version` temporarily as an additive header).
- Regression test: request `/health`, `/base/captcha`, and one authenticated endpoint; assert the GVA header name and value are present.
### P1: `/api/freshCasbin` cannot report the reference refresh failure
GVA reloads its in-memory Casbin enforcer and returns `刷新失败` if the enforcer is unavailable or policy loading fails. The local authorization implementation intentionally reads `casbin_rule` directly and turns the endpoint into an unconditional success no-op. Normal permission changes take effect correctly, but operators cannot receive the GVA failure response.
- Local: `internal/server/handler/api.go:201-206`.
- GVA: `server/api/v1/system/sys_api.go:314-329`, `server/service/system/sys_casbin.go:159-166`.
- Recommendation: document the no-cache implementation in the API migration notes, or provide a compatibility health check that can fail when policy storage is unavailable.
- Regression test: inject a failing policy-store read and verify the chosen compatibility behavior is intentional and stable.
### P3: empty `authorityIds` handling is intentionally safer locally
`POST /user/setUserAuthorities` accepts an empty array at the HTTP boundary in both implementations. The GVA service then indexes `authorityIds[0]` after deleting the old links (`server/service/system/sys_user.go:199-231`), which produces a panic/500 for an empty array. Kra returns a normal business error from `internal/biz/user.go:165-170` instead. This is safer behavior, but it is still a contract difference that clients/tests should make explicit.
- Recommendation: keep the safe local response and add a shared validation rule requiring at least one authority ID; do not reproduce the GVA panic.
- Regression test: submit `{"id": <user>, "authorityIds": []}` and assert a deterministic non-500 error with no role links lost.
## Endpoint-by-endpoint matrix
### Base, session, and token lifecycle
Route evidence: local `internal/server/router/public.go:9-16`, `internal/server/router/user.go:28-30`; GVA `server/router/system/sys_base.go:10-17`, `server/router/system/sys_jwt.go:9-14`.
| Endpoint | Binding, validation, authorization | Core logic / transaction / response | Result and handler evidence |
|---|---|---|---|
| `POST /base/captcha` | No body. Public; security limiter applies. Reads configured dimensions and IP failure count; starts count at 1 and enables captcha when `open==0` or failures exceed threshold. | Creates base64 digit captcha, persists it with configured expiry, returns `captchaId`, `picPath`, `captchaLength`, `openCaptcha`; same messages. | **P2** only for shared limiter key. Local `internal/server/handler/public.go:35-52`; GVA `server/api/v1/system/sys_captcha.go:29-69`. |
| `POST /base/login` | JSON `username/password/captcha/captchaId`; username and password required. Public; security limiter applies. | Same lock check, conditional captcha verification and counter increment, credential/disabled handling, login audit, password-expiry flag, token issue, multipoint old-token revocation/active-token replacement, cookie and response fields. | **P2** only for shared limiter key. Local `internal/server/handler/public.go:54-100`, `internal/biz/authentication.go:63-132`; GVA `server/api/v1/system/sys_user.go:26-167`. |
| `POST /jwt/jsonInBlacklist` | Private JWT/Casbin route; token read from `x-token`, then cookie fallback. | Persists revoked JWT and clears cookie; local checks blacklist directly on future authentication while GVA also fills cache. Client-visible invalidation and response match. | **OK**. Local `internal/server/handler/session.go:13-27`, `internal/data/api_token.go:128-130`; GVA `server/api/v1/system/sys_jwt_blacklist.go:20-35`, `server/service/system/jwt_black_list.go:24-35`. |
### User identity and profile
Route evidence: local `internal/server/router/user.go:9-30`, `internal/server/router/organization.go:18,28`; GVA `server/router/system/sys_user.go:10-30`.
| Endpoint | Binding, validation, authorization | Core logic / transaction / response | Result and handler evidence |
|---|---|---|---|
| `POST /user/getUserList` | JSON page/search; page and pageSize must be nonzero; private/Casbin. | Filters username/nickname/phone/email; allowed sort fields and default `id desc`; paginated `PageResult`. | **OK**. Local `internal/server/handler/user.go:24-44`, `internal/data/user.go:280-322`; GVA `server/api/v1/system/sys_user.go:185-219`, `server/service/system/sys_user.go:100-144`. |
| `POST /user/admin_register` | JSON username/password/nickname/authority; required fields and configured password policy; private/Casbin + operation record. | Hashes password; duplicate-username check and user/role link creation in a transaction; response envelope contains `user`. | **OK**. Local `internal/server/handler/user.go:46-81`, `internal/data/user.go:333-405`; GVA `server/api/v1/system/sys_user.go:170-183`, `server/service/system/sys_user.go:30-69`. |
| `PUT /user/setUserInfo` | JSON user fields; nonzero `ID`; private/Casbin + record. | If `authorityIds` supplied, replaces role links first; then updates mutable profile fields, preserving GVA's separate-write partial-failure behavior. | **OK**. Local `internal/server/handler/user.go:83-98`, `internal/biz/user.go:121-133`; GVA `server/api/v1/system/sys_user.go:321-339`. |
| `PUT /user/setSelfInfo` | JSON profile; identity comes from JWT; private/Casbin + record. | Updates caller-owned profile fields and returns `设置成功`. | **OK**. Local `internal/server/handler/user.go:100-117`, `internal/data/user.go:406-429`; GVA `server/api/v1/system/sys_user.go:341-362`, `server/service/system/sys_user.go:345-348`. |
| `DELETE /user/deleteUser` | JSON `id`, nonzero; blocks self-delete; private/Casbin + record. | Transaction soft-deletes user and deletes role/department/position links. | **OK**. Local `internal/server/handler/user.go:119-139`, `internal/data/user.go:493-510`; GVA `server/api/v1/system/sys_user.go:297-319`, `server/service/system/sys_user.go:301-318`. |
| `POST /user/resetPassword` | JSON ID/password; password policy; private/Casbin + record. | Rehashes, stamps `password_updated_at`, preserves `mustChangePassword` state as GVA does. | **OK**. Local `internal/server/handler/user.go:141-156`, `internal/biz/user.go:140-149`; GVA `server/api/v1/system/sys_user.go:410-431`, `server/service/system/sys_user.go:414-421`. |
| `POST /user/changePassword` | JSON old/new password; both required; JWT identity; password-expiry guard allows this route. | Verifies old hash, validates new password, rehashes, updates timestamp and clears forced-change flag. | **OK**. Local `internal/server/handler/user.go:158-186`, `internal/biz/user.go:150-164`; GVA `server/api/v1/system/sys_user.go:71-94`, `server/service/system/sys_user.go:75-94`. |
| `PUT /user/setSelfSetting` | JSON map; JWT identity; private/Casbin + record. | Marshals and stores `origin_setting`; same success envelope. | **OK**. Local `internal/server/handler/user.go:188-204`, `internal/data/user.go:563-569`; GVA `server/api/v1/system/sys_user.go:345-362`. |
| `POST /user/setUserAuthorities` | JSON target ID and authority IDs; private/Casbin + record. | For non-empty IDs, transaction replaces role links and sets first supplied role as primary; strict-role guard is enforced for submitted role IDs. Empty IDs return a deterministic local business error, while GVA panics after deleting links. | **P3** safer-input divergence. Local `internal/server/handler/user.go:206-217`, `internal/biz/user.go:165-170`, `internal/data/user.go:512-544`; GVA `server/api/v1/system/sys_user.go:195-230`, `server/service/system/sys_user.go:199-232`. |
| `POST /user/setUserAuthority` | JSON authority ID required; caller identity from JWT; private/Casbin + record. | Verifies membership and default router, updates active authority, signs a same-expiry replacement token, writes `new-token`/`new-expires-at` and cookie. | **OK**. Local `internal/server/handler/user.go:219-244`, `internal/biz/authentication.go:138-153`; GVA `server/api/v1/system/sys_user.go:146-193`, `server/service/system/sys_user.go:150-193`. |
| `GET /user/getUserInfo` | No body; JWT identity; private/Casbin and password-expiry guard allows it. | Loads by JWT UUID with authorities, primary/multiple departments, positions, default-router fallback, and name paths. | **OK**. Local `internal/server/handler/user.go:246-258`, `internal/data/user.go:44-97`; GVA `server/api/v1/system/sys_user.go:364-408`, `server/service/system/sys_user.go:368-409`. |
| `POST /user/setUserDepartments` | JSON user ID, `deptIds`, `primaryDeptId`; nonzero user ID; private/Casbin + record. | Transaction replaces department links; selects first department as missing primary and rejects a primary outside the submitted set. | **OK**. Local `internal/server/handler/organization.go:119-134`, `internal/data/department.go:242-274`; GVA `server/api/v1/system/sys_user.go:234-274`, `server/service/system/sys_user.go:236-274`. |
| `POST /user/setUserPositions` | JSON user ID and position IDs; nonzero user ID; private/Casbin + record. | Transaction replaces user-position links. | **OK**. Local `internal/server/handler/organization.go:238-253`, `internal/data/position.go:133-147`; GVA `server/api/v1/system/sys_user.go:276-295`, `server/service/system/sys_user.go:277-295`. |
### Authorities and data scope
Route evidence: local `internal/server/router/authority.go:9-20`; GVA `server/router/system/sys_authority.go:10-26`.
| Endpoint | Binding, validation, authorization | Core logic / transaction / response | Result and handler evidence |
|---|---|---|---|
| `POST /authority/getAuthorityList` | No body; private/Casbin. | Builds tree under current authority; applies strict-tree visibility, including non-root direct children behavior. | **OK**. Local `internal/server/handler/authority.go:15-21`, `internal/data/authority.go:300-342`; GVA `server/api/v1/system/sys_authority.go:163-181`, `server/service/system/sys_authority.go:151-186`. |
| `POST /authority/createAuthority` | JSON authority ID/name required; strict mode reparents a root request to actor; private/Casbin + record. | Transaction checks duplicate, applies defaults, seeds dashboard menu and the nine default policies; success contains `authority`. | **OK**. Local `internal/server/handler/authority.go:23-42`, `internal/data/authority.go:72-114`; GVA `server/api/v1/system/sys_authority.go:26-65`, `server/service/system/sys_authority.go:32-74`, `server/model/system/request/sys_casbin.go:15-26`. |
| `POST /authority/copyAuthority` | JSON old/new authority; both IDs and new name required; private/Casbin + record. | Transaction copies assigned menus, buttons and deduplicated policies; strict mode prevents copying out-of-scope policy paths. | **OK**. Local `internal/server/handler/authority.go:44-67`, `internal/data/authority.go:115-241`; GVA `server/api/v1/system/sys_authority.go:67-101`, `server/service/system/sys_authority.go:76-123`. |
| `PUT /authority/updateAuthority` | JSON authority ID/name required; private/Casbin + record. | Looks up target then updates authority metadata and returns it under `authority`. | **OK**. Local `internal/server/handler/authority.go:69-88`, `internal/data/authority.go:242-250`; GVA `server/api/v1/system/sys_authority.go:133-161`, `server/service/system/sys_authority.go:125-145`. |
| `POST /authority/deleteAuthority` | JSON authority ID required; private/Casbin + record. | Transaction blocks users/primary users/children, clears user/menu/button/policy links, permanently removes authority row. | **OK**. Local `internal/server/handler/authority.go:90-104`, `internal/data/authority.go:251-299`; GVA `server/api/v1/system/sys_authority.go:103-131`, `server/service/system/sys_authority.go:127-149`. |
| `POST /authority/setRoleUsers` | JSON authority ID required; private/Casbin + record. | Transaction replaces role-user links; changes primary role of removed users only when another role remains. | **OK**. Local `internal/server/handler/authority.go:106-120`, `internal/data/authority.go:343-385`; GVA `server/api/v1/system/sys_authority.go:263-286`, `server/service/system/sys_authority.go:303-367`. |
| `GET /authority/getUsersByAuthority` | Query `authorityId`; private/Casbin. | Returns empty array rather than null. | **OK**. Local `internal/server/handler/authority.go:122-136`; GVA `server/api/v1/system/sys_authority.go:236-261`. |
| `POST /authority/setDataScope` | JSON authority ID required; private/Casbin + record. | Transaction updates `data_scope`, clears old custom departments, inserts submitted departments only for scope 5. | **OK**. Local `internal/server/handler/authority.go:138-152`, `internal/data/authority.go:390-409`; GVA `server/api/v1/system/sys_authority.go:183-207`, `server/service/system/sys_authority.go:238-262`. |
| `GET /authority/getDataScopeDepts` | Query `authorityId`; private/Casbin. | Returns custom department IDs, normalizing nil to `[]`. | **OK**. Local `internal/server/handler/authority.go:154-168`; GVA `server/api/v1/system/sys_authority.go:209-234`, `server/service/system/sys_authority.go:264-270`. |
### Menu and navigation
Route evidence: local `internal/server/router/menu.go:9-21`, `internal/server/router/user.go:24-26`; GVA `server/router/system/sys_menu.go:10-29`.
| Endpoint | Binding, validation, authorization | Core logic / transaction / response | Result and handler evidence |
|---|---|---|---|
| `POST /menu/getMenu` | Empty body; JWT authority; private/Casbin. | Reads role-assigned menus, ordered tree, parameters and selected button map; returns `{menus}`. | **OK**. Local `internal/server/handler/navigation.go:14-28`, `internal/data/user.go:197-278`; GVA `server/api/v1/system/sys_menu.go:25-44`, `server/service/system/sys_menu.go:30-87`. |
| `POST /menu/getMenuList` | No effective body fields; private/Casbin. | Produces strict-aware base-menu tree with parameters/buttons. | **OK**. Local `internal/server/handler/menu.go:15-22`, `internal/data/menu.go:181-243`; GVA `server/api/v1/system/sys_menu.go:325-342`, `server/service/system/sys_menu.go:89-121`. |
| `POST /menu/getBaseMenuTree` | Empty body; private/Casbin. | Same base tree, wrapped as `{menus}`. | **OK**. Local `internal/server/handler/menu.go:24-31`; GVA `server/api/v1/system/sys_menu.go:46-64`. |
| `POST /menu/addBaseMenu` | JSON path/name/component/meta.title required; nonnegative sort; private/Casbin + record. | Transaction rejects duplicate name, validates parent and default-router constraints, inserts menu then parameters/buttons. | **OK**. Local `internal/server/handler/menu.go:33-64`, `internal/data/menu.go:60-99`; GVA `server/api/v1/system/sys_menu.go:125-158`, `server/service/system/sys_menu.go:123-171`. |
| `POST /menu/updateBaseMenu` | Same validation as create; private/Casbin + record. | Transaction finds menu, checks renamed duplicate, replaces parameters/buttons, updates scalar fields. | **OK**. Local `internal/server/handler/menu.go:66-97`, `internal/data/menu.go:101-122`; GVA `server/api/v1/system/sys_menu.go:190-223`, `server/service/system/sys_base_menu.go`. |
| `POST /menu/deleteBaseMenu` | JSON nonzero ID; private/Casbin + record. | Transaction blocks children/default-router use, then deletes menu, relations and permission buttons. | **OK**. Local `internal/server/handler/menu.go:99-114`, `internal/data/menu.go:124-155`; GVA `server/api/v1/system/sys_menu.go:160-188`. |
| `POST /menu/getBaseMenuById` | JSON nonzero ID; private/Casbin + record. | Loads menu with parameters/buttons and returns `{menu}`. | **OK**. Local `internal/server/handler/menu.go:116-132`, `internal/data/menu.go:158-180`; GVA `server/api/v1/system/sys_menu.go:225-253`. |
| `POST /menu/addMenuAuthority` | JSON authority ID required; private/Casbin + record. | Strict-mode target/menu scope checks, then transaction replaces role-menu links. | **OK**. Local `internal/server/handler/menu.go:134-149`, `internal/data/menu.go:259-309`; GVA `server/api/v1/system/sys_menu.go:66-93`, `server/service/system/sys_menu.go:202-241`. |
| `POST /menu/getMenuAuthority` | JSON authority ID required; private/Casbin. | Reads target role's own assigned base menus without applying caller's listing filter; response `{menus}`. | **OK**. Local `internal/server/handler/menu.go:151-167`, `internal/data/menu.go:313-331`; GVA `server/api/v1/system/sys_menu.go:95-123`. |
| `GET /menu/getMenuRoles` | Query nonzero `menuId`; private/Casbin. | Returns role IDs and `defaultRouterAuthorityIds`, normalizing nil arrays. | **OK**. Local `internal/server/handler/menu.go:169-196`, `internal/data/menu.go:333-346`; GVA `server/api/v1/system/sys_menu.go:255-296`. |
| `POST /menu/setMenuRoles` | JSON nonzero `menuId`; private/Casbin + record. | Transaction replaces all role-menu rows. | **OK**. Local `internal/server/handler/menu.go:198-212`, `internal/data/menu.go:348-364`; GVA `server/api/v1/system/sys_menu.go:298-323`, `server/service/system/sys_menu.go`. |
### API registry and Casbin policy management
Route evidence: local `internal/server/router/api.go:9-28`; GVA `server/router/system/sys_api.go:10-35`, `server/router/system/sys_casbin.go:10-19`.
| Endpoint | Binding, validation, authorization | Core logic / transaction / response | Result and handler evidence |
|---|---|---|---|
| `POST /api/getApiList` | JSON page/pageSize nonzero plus filters/order; private/Casbin. | Same filters, allowed sort whitelist and paged `PageResult`. | **OK**. Local `internal/server/handler/api.go:15-34`, `internal/data/api.go:121-181`; GVA `server/api/v1/system/sys_api.go:177-210`, `server/service/system/sys_api.go:185-232`. |
| `POST /api/getAllApis` | No body; private/Casbin. | Returns all APIs ordered `id desc`, but strict non-root caller only sees APIs permitted by own policies. | **OK**. Local `internal/server/handler/api.go:36-42`, `internal/data/api.go:121-152`; GVA `server/api/v1/system/sys_api.go:271-289`, `server/service/system/sys_api.go:239-270`. |
| `POST /api/createApi` | JSON path/description/apiGroup/method all required; private/Casbin + record. | Duplicate path+method rejection; creates and returns populated API row. | **OK**. Local `internal/server/handler/api.go:44-59`, `internal/data/api.go:55-69`; GVA `server/api/v1/system/sys_api.go:26-53`, `server/service/system/sys_api.go:26-33`. |
| `POST /api/updateApi` | Same required fields; private/Casbin + record. | Finds target, rejects duplicate changed path/method, updates matching policies first, then API metadata. | **OK**. Local `internal/server/handler/api.go:61-75`, `internal/data/api.go:70-94`; GVA `server/api/v1/system/sys_api.go:242-269`, `server/service/system/sys_api.go:278-304`. |
| `POST /api/deleteApi` | JSON nonzero ID; private/Casbin + record. | Requires existing row then deletes it and matching policy/legacy relation rows. | **OK**. Local `internal/server/handler/api.go:77-91`, `internal/biz/api.go:39-47`, `internal/data/api.go:95-120`; GVA `server/api/v1/system/sys_api.go:147-175`, `server/service/system/sys_api.go:165-177`. |
| `DELETE /api/deleteApisByIds` | JSON ID slice; private/Casbin + record. | Transaction deletes selected rows and matching policies; empty/missing ID behavior follows GVA bulk semantics. | **OK**. Local `internal/server/handler/api.go:93-103`, `internal/data/api.go:95-120`; GVA `server/api/v1/system/sys_api.go:291-312`, `server/service/system/sys_api.go:312-332`. |
| `POST /api/getApiById` | JSON nonzero ID; private/Casbin + record. | Finds one API and wraps it as `{api}`. | **OK**. Local `internal/server/handler/api.go:105-120`; GVA `server/api/v1/system/sys_api.go:212-240`. |
| `GET /api/getApiGroups` | No body; private/Casbin + record. | Natural-ID scan produces first-seen group list and path-segment map. | **OK**. Local `internal/server/handler/api.go:122-128`, `internal/service/api.go:64-86`; GVA `server/api/v1/system/sys_api.go:77-96`, `server/service/system/sys_api.go:35-56`. |
| `GET /api/getApiRoles` | Query path and method required; private/Casbin. | Reads role IDs from `p` policies; nil becomes `[]`. | **OK**. Local `internal/server/handler/api.go:130-144`, `internal/data/api.go:182-201`; GVA `server/api/v1/system/sys_api.go:334-360`, `server/service/system/sys_casbin.go:169-184`. |
| `POST /api/setApiRoles` | JSON path/method required; private/Casbin + record. | Transaction removes all matching policy rows then inserts the requested roles; GVA cache reload is unnecessary locally because policy is DB-read. | **OK**. Local `internal/server/handler/api.go:146-161`, `internal/data/api.go:202-222`; GVA `server/api/v1/system/sys_api.go:362-389`, `server/service/system/sys_casbin.go:186-206`. |
| `GET /api/syncApi` | No body; private/Casbin + record. | Compares engine routes to API rows and ignore rows; returns all arrays as `newApis/deleteApis/ignoreApis`. | **OK**. Local `internal/server/handler/api.go:163-178`, `internal/biz/api.go:50-95`; GVA `server/api/v1/system/sys_api.go:55-75`, `server/service/system/sys_api.go:58-130`. |
| `POST /api/ignoreApi` | JSON path/method/flag; private/Casbin + record. | Inserts duplicate ignore row when flag true; permanently deletes all matching ignore rows when false. | **OK**. Local `internal/server/handler/api.go:180-191`, `internal/data/api.go:335-350`; GVA `server/api/v1/system/sys_api.go:98-120`, `server/service/system/sys_api.go:132-137`. |
| `POST /api/enterSyncApi` | JSON submitted added/deleted arrays; private/Casbin + record. | Transaction inserts submitted additions and deletes matching APIs/policies. | **OK**. Local `internal/server/handler/api.go:193-199`, `internal/data/api.go:351-384`; GVA `server/api/v1/system/sys_api.go:122-145`, `server/service/system/sys_api.go:139-157`. |
| `GET /api/freshCasbin` | Public, no body. | GVA reloads cache; local is a DB-policy no-op. | **P1**; see finding. Local `internal/server/handler/api.go:201-206`; GVA `server/api/v1/system/sys_api.go:314-329`. |
| `POST /casbin/updateCasbin` | JSON nonzero authority ID; private/Casbin + record. | Enforces strict target and policy scope, clears old authority policies, deduplicates and adds replacement paths. | **OK**. Local `internal/server/handler/api.go:208-222`, `internal/data/api.go:261-334`; GVA `server/api/v1/system/sys_casbin.go:23-52`, `server/service/system/sys_casbin.go:29-89`. |
| `POST /casbin/getPolicyPathByAuthorityId` | JSON nonzero authority ID; private/Casbin. | Returns `{paths}` with path/method policy pairs. Local DB query avoids GVA cache staleness in normal endpoint flows. | **OK**. Local `internal/server/handler/api.go:224-239`, `internal/data/api.go:238-260`; GVA `server/api/v1/system/sys_casbin.go:54-79`, `server/service/system/sys_casbin.go:121-140`. |
### Authority button permissions
Route evidence: local `internal/server/router/permission.go:9-14`; GVA `server/router/system/sys_authority_btn.go:11-19`.
| Endpoint | Binding, validation, authorization | Core logic / transaction / response | Result and handler evidence |
|---|---|---|---|
| `POST /authorityBtn/getAuthorityBtn` | JSON authority/menu IDs; private/Casbin. | Loads selected base-button IDs and returns `{selected}`. | **OK**. Local `internal/server/handler/permission.go:17-29`, `internal/data/permission.go:58-75`; GVA `server/api/v1/system/sys_authority_btn.go:21-44`, `server/service/system/sys_authority_btn.go:18-34`. |
| `POST /authorityBtn/setAuthorityBtn` | JSON authority/menu IDs and selection; private/Casbin. | Transaction deletes current selection for that authority/menu and inserts submitted IDs. | **OK**. Local `internal/server/handler/permission.go:31-42`, `internal/data/permission.go:76-90`; GVA `server/api/v1/system/sys_authority_btn.go:46-68`, `server/service/system/sys_authority_btn.go:36-63`. |
| `POST /authorityBtn/canRemoveAuthorityBtn` | Query `id`; private/Casbin. | Succeeds only when no authority-button reference exists; same failure message. | **OK**. Local `internal/server/handler/permission.go:44-55`, `internal/data/permission.go:91-100`; GVA `server/api/v1/system/sys_authority_btn.go:70-85`, `server/service/system/sys_authority_btn.go:65-74`. |
### Departments and positions
Route evidence: local `internal/server/router/organization.go:9-29`; GVA `server/router/system/sys_department.go:10-24`, `server/router/system/sys_position.go:10-24`.
| Endpoint | Binding, validation, authorization | Core logic / transaction / response | Result and handler evidence |
|---|---|---|---|
| `POST /department/getDepartmentList` | JSON search is deliberately best-effort; private/Casbin. | Name search returns flat list; no search builds sorted recursive tree with leaders. | **OK**. Local `internal/server/handler/organization.go:20-28`, `internal/data/department.go:173-219`; GVA `server/api/v1/system/sys_department.go:100-119`, `server/service/system/sys_department.go:151-187`. |
| `POST /department/createDepartment` | JSON name required; private/Casbin + record. | Resolves parent, derives ancestors (`0` root), and inserts department. | **OK**. Local `internal/server/handler/organization.go:30-44`, `internal/data/department.go:85-100`; GVA `server/api/v1/system/sys_department.go:23-48`, `server/service/system/sys_department.go:88-100`. |
| `PUT /department/updateDepartment` | JSON nonzero ID; private/Casbin + record. | Rejects self-parent, validates parent, recomputes this node's ancestors only. | **OK**. Local `internal/server/handler/organization.go:46-60`, `internal/data/department.go:101-120`; GVA `server/api/v1/system/sys_department.go:50-75`, `server/service/system/sys_department.go:102-121`. |
| `DELETE /department/deleteDepartment` | JSON ID; private/Casbin + record. | Blocks children, primary-department users and join-table users before deleting. | **OK**. Local `internal/server/handler/organization.go:62-72`, `internal/data/department.go:121-149`; GVA `server/api/v1/system/sys_department.go:77-98`, `server/service/system/sys_department.go:123-149`. |
| `GET /department/findDepartment` | Query ID; private/Casbin. | Loads one department with leader. | **OK**. Local `internal/server/handler/organization.go:74-85`, `internal/data/department.go:150-172`; GVA `server/api/v1/system/sys_department.go:121-142`, `server/service/system/sys_department.go:151-153`. |
| `GET /department/getDepartmentUsers` | Query department ID; private/Casbin. | Reads join-table user IDs; nil normalizes to `[]`. | **OK**. Local `internal/server/handler/organization.go:87-101`, `internal/data/department.go:220-225`; GVA `server/api/v1/system/sys_department.go:144-169`, `server/service/system/sys_department.go:190-195`. |
| `POST /department/setDepartmentUsers` | JSON nonzero department ID; private/Casbin + record. | Transaction replaces membership; clears removed primary department and sets the department for additions without one. | **OK**. Local `internal/server/handler/organization.go:103-117`, `internal/data/department.go:226-241`; GVA `server/api/v1/system/sys_department.go:171-194`, `server/service/system/sys_department.go:197-243`. |
| `POST /position/getPositionList` | JSON search; no page validation by either implementation; private/Casbin. | Same name/code/status filters, `sort` ordering, raw supplied limit/offset and `PageResult`. | **OK**. Local `internal/server/handler/organization.go:136-147`, `internal/data/position.go:77-111`; GVA `server/api/v1/system/sys_position.go:100-127`, `server/service/system/sys_position.go:76-101`. |
| `POST /position/createPosition` | JSON name required; private/Casbin + record. | Inserts name/code/sort/status/remark. | **OK**. Local `internal/server/handler/organization.go:149-163`, `internal/data/position.go:51-53`; GVA `server/api/v1/system/sys_position.go:23-48`, `server/service/system/sys_position.go:19-22`. |
| `PUT /position/updatePosition` | JSON nonzero ID; private/Casbin + record. | Updates name/code/sort/status/remark. | **OK**. Local `internal/server/handler/organization.go:165-179`, `internal/data/position.go:54-56`; GVA `server/api/v1/system/sys_position.go:50-75`, `server/service/system/sys_position.go:24-36`. |
| `DELETE /position/deletePosition` | JSON ID; private/Casbin + record. | Blocks existing user-position links, then soft-deletes. | **OK**. Local `internal/server/handler/organization.go:181-191`, `internal/data/position.go:57-70`; GVA `server/api/v1/system/sys_position.go:77-98`, `server/service/system/sys_position.go:38-51`. |
| `GET /position/findPosition` | Query ID; private/Casbin. | Loads one position. | **OK**. Local `internal/server/handler/organization.go:193-204`, `internal/data/position.go:71-76`; GVA `server/api/v1/system/sys_position.go:129-150`, `server/service/system/sys_position.go:53-57`. |
| `GET /position/getPositionUsers` | Query position ID; private/Casbin. | Reads user IDs and normalizes nil to `[]`. | **OK**. Local `internal/server/handler/organization.go:206-220`, `internal/data/position.go:112-117`; GVA `server/api/v1/system/sys_position.go:152-177`, `server/service/system/sys_position.go:103-108`. |
| `POST /position/setPositionUsers` | JSON nonzero position ID; private/Casbin + record. | Transaction replaces all links for target position. | **OK**. Local `internal/server/handler/organization.go:222-236`, `internal/data/position.go:118-132`; GVA `server/api/v1/system/sys_position.go:179-202`, `server/service/system/sys_position.go:110-130`. |
## Recommended validation plan
1. **Contract tests**: table-drive every route above for method/path, envelope, success message, and required-field failure. Add the two response-header cases and public `freshCasbin` behavior.
2. **Shared-cache migration tests**: run both binaries against one Redis/cache instance and verify captcha/login rate limits, captcha values, multipoint session keys, and JWT blacklist behavior across the boundary.
3. **Authority/Casbin transaction tests**: duplicate policies, strict non-root role assignment, copy authority, update API path/method, sync deletion, and role/menu/button replacement with injected write failures.
4. **Organization/data-scope tests**: multi-department primary reassignment, department move ancestors, scope 1/2/4/5 read/write filtering, and blocked-write audit events.
5. **Manual smoke suite**: login -> switch role -> load menus -> assign API/menu/button policies -> refresh/verify authorization -> logout -> assert old token rejection.
## Conclusion
All audited migrated identity/access endpoints match GVA's route, validation, permission, data mutation, transaction, and JSON-envelope behavior under normal operation. The scoped identity/access findings are: the P2 rate-limit cache prefix, the P2 version response-header name, the P1 `freshCasbin` failure-semantics difference, and the P3 safer handling of an empty `authorityIds` array. Cross-cutting route-table/static-file, CORS, JWT-store, and router-prefix synchronization findings are tracked in `parity-crosscut.md` rather than duplicated here.