fix(n+1): 批量查询替代循环单查
- IsAdminBootstrapRequired: userRepo.GetByID 循环 → GetByIDs 批量 - AssignRoles: roleRepo.GetByID 循环 → GetByIDs 批量 - 在 userRepositoryInterface 补充 GetByIDs 方法签名
This commit is contained in:
@@ -231,3 +231,34 @@ Use this section first if earlier 2026-04-23 notes in this file conflict with it
|
||||
|
||||
- [ ] If a UI form shape differs from the backend write contract, the service adapter must serialize the backend field names explicitly and service tests must pin the exact outbound payload.
|
||||
- [ ] If a browser runner waits on in-page fetch diagnostics, that wait must be created in the same control flow as the submit action and must not be allowed to outlive a failed click or fill step.
|
||||
|
||||
## 2026-04-24 Scenario-Isolated Browser Gate Snapshot
|
||||
|
||||
### Latest Green Evidence
|
||||
|
||||
- `cd frontend/admin && npm.cmd run test:run -- src/lib/playwright-e2e-scenarios.test.ts`
|
||||
- `cd frontend/admin && npm.cmd run test:run`
|
||||
- `cd frontend/admin && npm.cmd run lint`
|
||||
- `cd frontend/admin && npm.cmd run build`
|
||||
- `cd frontend/admin && $env:E2E_SCENARIOS='email-activation'; npm.cmd run e2e:full:win`
|
||||
- `cd frontend/admin && npm.cmd run e2e:full:win`
|
||||
|
||||
### Current Honest Release Conclusion
|
||||
|
||||
- The supported browser-level gate is green again in the current workspace after changing the wrapper to run each scenario in a fresh browser process while keeping one real backend and one real test database alive.
|
||||
- The latest green full run executed `21` isolated scenario runs: `admin-bootstrap` plus the `20` steady-state scenarios behind it.
|
||||
- This evidence proves the documented browser-level acceptance path in the current workspace. It does not by itself prove that the underlying Chromium host-runtime `0x5` issue has disappeared.
|
||||
|
||||
### Additional Checklist Items
|
||||
|
||||
- [ ] If the host browser runtime is the unstable component, isolate browser processes per scenario before expanding suite-level retries.
|
||||
- [ ] If the supported gate uses scenario isolation, the wrapper still preserves one real backend, one real frontend server, one real SMTP capture path, and one real test database for the whole run.
|
||||
- [ ] The scenario list used by the wrapper is derived from the same source as the Playwright runner and is not duplicated manually in release-critical code.
|
||||
|
||||
## 2026-04-24 Resource Ownership Authorization Snapshot
|
||||
|
||||
### Additional Checklist Items
|
||||
|
||||
- [ ] For any owner-scoped resource endpoint addressed by path ID, verify that a non-owner cannot read, update, delete, or privilege-toggle another user's resource through the supported API surface.
|
||||
- [ ] For the same endpoint family, verify that the service layer re-checks ownership or admin privilege instead of trusting only a handler-level path check.
|
||||
- [ ] When admin cross-user access is intentional, add one positive regression proving the admin path still works after the IDOR fix.
|
||||
|
||||
@@ -322,3 +322,27 @@ Use this section as the newest summary of what changed in the workspace after th
|
||||
- Service tests must assert the serialized write payload, not only the UI form model. Otherwise the test suite can lock in the wrong contract and make the browser suite the first honest signal.
|
||||
- Orphaned async diagnostics waste debugging time. A failed click or fill should not leave a background fetch waiter alive long enough to crash during cleanup and hide the real failing step.
|
||||
- A targeted scenario recovery is still not enough evidence on its own. The `profile-and-security` fix only counted after `cd frontend/admin && npm.cmd run e2e:full:win` returned green on the same workspace state.
|
||||
## 2026-04-24 Profile Contract And Gate Reality Lessons
|
||||
|
||||
- A green profile page in mocked tests does not prove the real user-detail contract. This round's browser flow only closed after the backend `PUT /users/:id` handler stopped silently dropping `gender`, `birthday`, `region`, and `bio`.
|
||||
- Detail endpoints must return the fields their edit pages re-hydrate after save. Returning only an ID, username, email, and nickname is not a harmless optimization when the page immediately re-fetches the record and expects the full profile shape.
|
||||
- A targeted official browser sub-gate is valid evidence for the repaired workflow, but it is not evidence that the whole supported browser gate is green. The honest split on 2026-04-24 was:
|
||||
- `profile-management` passed through the supported `e2e:full:win` entrypoint with scenario filtering.
|
||||
- The unfiltered main gate remained blocked by the pre-existing `admin-bootstrap` headless-shell disconnect.
|
||||
- Wrapper drift matters. Restoring the documented Windows crashpad/noerrdialogs launch args and moving the headless-shell profile dir out of the repo tree reduced noise enough for the real product defect to surface.
|
||||
|
||||
## 2026-04-24 Scenario-Isolated Browser Orchestration Lessons
|
||||
|
||||
- When Chromium-family browsers all show the same host-level `crashpad` or `mojo platform_channel` access-denied signals, it is no longer rigorous to keep treating every E2E collapse as a product bug.
|
||||
- Shared backend state does not require a shared browser process. The stable recovery here was: keep one real backend, one real frontend dev server, one real SMTP capture file, and one real SQLite database, but give each scenario a fresh browser process.
|
||||
- If the browser is the unstable component, retry at the scenario boundary, not by replaying an ever-growing multi-scenario browser session from the top each time.
|
||||
- The wrapper and the runner must not maintain separate hard-coded scenario lists. Once filter behavior and full-gate behavior drift, targeted green runs stop being trustworthy evidence for the supported entrypoint.
|
||||
|
||||
## 2026-04-24 Device IDOR Closure Lessons
|
||||
|
||||
- A handler-level auth check on a sibling route does not protect the rest of a resource family. `GET /devices/users/:id` was already restricted while `/devices/:id*` still trusted raw device IDs and remained vulnerable.
|
||||
- Ownership-sensitive APIs need actor-aware service entry points. Passing only a resource ID into a generic service method leaves the next handler or admin-route reuse free to bypass the original intent.
|
||||
- The fastest honest security closure was red-green at both layers:
|
||||
- handler regressions proved a normal user could read and mutate another user's device through the real HTTP surface;
|
||||
- service regressions proved no owner/admin authorization API existed yet;
|
||||
- the fix only counted after both targeted regressions, the backend full matrix, and the supported browser gate were green on the same branch state.
|
||||
|
||||
@@ -399,3 +399,14 @@ Use this section as the current supplement when older sections do not cover perm
|
||||
- If a form includes UI-only fields such as `confirm_password`, outbound service code must strip or remap those fields before hitting the API. UI form names are not a valid substitute for the backend write contract.
|
||||
- Service regression tests for write paths must assert the exact payload sent into the shared HTTP client, not only the values collected from the component or form layer.
|
||||
- Browser-runner fetch or response waiters must be action-scoped. A waiter that can outlive a failed action and later crash with a page-closed error is not acceptable verification infrastructure.
|
||||
|
||||
## 2026-04-24 Scenario-Isolated Browser Gate Supplement
|
||||
|
||||
- The supported Windows browser gate may share one real backend and one real test database while still isolating browser processes per scenario. Reusing a single long-lived browser is not a quality requirement when the browser runtime itself is the unstable component.
|
||||
- If browser-runtime instability is external to the product and reproducible across Chromium variants, recover at the scenario boundary with a fresh browser before classifying the supported gate as inherently flaky.
|
||||
- The supported wrapper and the Playwright runner must derive selected scenario names from one shared source of truth. Duplicated scenario lists are a governance bug because they can make filtered evidence disagree with the documented main gate.
|
||||
|
||||
## 2026-04-24 Resource Ownership Authorization Supplement
|
||||
|
||||
- A path parameter is never sufficient authorization for an owner-scoped resource. For endpoints such as `/devices/:id`, `/users/:id/password`, and similar resource-by-id APIs, the handler must pass actor identity into the service layer and the service layer must re-check ownership or admin privilege before reading or mutating the resource.
|
||||
- IDOR regression coverage for owner-scoped resources must include at least one non-owner read attempt, one non-owner mutation attempt, one non-owner destructive attempt, and one privileged state-change attempt such as trust, status, or reset semantics. Include one admin positive path when admin access is part of the contract.
|
||||
|
||||
@@ -251,3 +251,59 @@ Use this section as the newest technical snapshot when earlier 2026-04-23 notes
|
||||
### Stability Rule
|
||||
|
||||
- When a scenario uses asynchronous fetch diagnostics for proof, create the waiter in the same control flow as the triggering action and tear it down implicitly with that action path. A background waiter that survives a failed action is a runner bug because it can replace the primary failure with misleading page-closed noise.
|
||||
|
||||
## 2026-04-24 Scenario-Isolated Browser Gate Notes
|
||||
|
||||
### Main Acceptance Path
|
||||
|
||||
- The supported browser-level gate remains `cd frontend/admin && npm.cmd run e2e:full:win`.
|
||||
- On 2026-04-24 that gate was re-run green after changing `frontend/admin/scripts/run-playwright-auth-e2e.ps1` to keep one backend and one frontend session alive while launching a fresh browser process for each selected Playwright scenario.
|
||||
- `frontend/admin/scripts/run-playwright-cdp-e2e.mjs` now supports a lightweight `E2E_LIST_SCENARIOS=1` mode so the wrapper and the runner derive the scenario order from the same source of truth.
|
||||
|
||||
### Current Green Evidence
|
||||
|
||||
- The current full-gate green evidence is `21` isolated scenario runs in one end-to-end environment:
|
||||
- `admin-bootstrap`
|
||||
- `public-registration`
|
||||
- `email-activation`
|
||||
- `password-reset`
|
||||
- `login-surface`
|
||||
- `auth-workflow`
|
||||
- `responsive-login`
|
||||
- `desktop-mobile-navigation`
|
||||
- `user-management-crud`
|
||||
- `user-management-batch`
|
||||
- `role-management-crud`
|
||||
- `permissions-management-crud`
|
||||
- `device-management`
|
||||
- `login-logs`
|
||||
- `operation-logs`
|
||||
- `webhook-management`
|
||||
- `import-export`
|
||||
- `profile-management`
|
||||
- `profile-and-security`
|
||||
- `settings`
|
||||
- `dashboard-stats`
|
||||
|
||||
### Operational Knobs
|
||||
|
||||
- `E2E_SCENARIO_ISOLATION=0` keeps the legacy whole-suite browser mode available for diagnostics.
|
||||
- `E2E_SCENARIO_ATTEMPTS` overrides the per-scenario retry count; otherwise the wrapper falls back to `E2E_SUITE_ATTEMPTS`.
|
||||
|
||||
## 2026-04-24 Resource Ownership Authorization Notes
|
||||
|
||||
### Recommended Implementation Pattern
|
||||
|
||||
- For owner-scoped resources, split authorization across two layers:
|
||||
- the handler extracts the current actor identity and admin bit from the authenticated request context;
|
||||
- the service loads the target resource and re-checks `owner-or-admin` before returning or mutating it.
|
||||
- This prevents future handlers, background callers, or admin-route reuse from silently bypassing ownership checks by passing a raw resource ID straight into repository operations.
|
||||
|
||||
### Minimum Regression Pattern
|
||||
|
||||
- Add a targeted red-green regression set for each resource family that covers:
|
||||
- cross-user read forbidden;
|
||||
- cross-user update forbidden;
|
||||
- cross-user delete forbidden;
|
||||
- cross-user state toggle forbidden;
|
||||
- admin positive access when the contract allows it.
|
||||
|
||||
Reference in New Issue
Block a user