- Create user/ - User guides and configuration documentation - Create features/ - Feature specifications and business flows - Create debugging/ - Debug guides and quick references - Create testing/ - Test infrastructure, reports, and plans - Create internal/ - Internal plans, analyses, and templates - Move cleaner/*.md to cleaner/ directory - Move LOGGING_*.md to developer/guides/ Add docs/README.md as documentation index with category navigation and quick lookup guide. The reorganized structure makes it easier for users and developers to quickly locate relevant documentation.
7.8 KiB
7.8 KiB
CleanerPage User Scope Fix
Issue: User users were affecting other users' data when using "取消" and "确认删除" buttons
Date: 2026-03-03
Branch: fix/cleaner-user-scope
Problem Analysis
Bug Description
For User type (non-Admin) users:
- The table shows only materials assigned to the current user (filtered by
filteredResults) - Clicking "取消" (Uncheck All) was unchecking ALL materials in
validationResults, including invisible ones - Clicking "确认删除" (Confirm Deletion) processed ALL materials in
validationResults, not just visible ones - This caused User A to delete User B's materials that User A never saw!
Root Causes
1. "取消" Button (Line 420)
// ❌ WRONG: Clears ALL selected items
onClick={() => setSelectedItems(new Set())}
2. handleConfirmDeletion Function (Line 165)
// ❌ WRONG: Iterates ALL validation results
for (const result of validationResults) {
// Processes items user can't even see!
}
Data Flow
graph TB
subgraph "Backend"
A[validationResults<br/>1000 items] --> B[User Filter<br/>currentUsername]
end
subgraph "Frontend Display"
B --> C[filteredResults<br/>100 items visible]
C --> D[Table Display]
end
subgraph "Bug Behavior (BEFORE FIX)"
E[取消 Button] --> F[Clears selectedItems<br/>for ALL 1000 items ❌]
G[确认删除 Button] --> H[Processes ALL 1000 items ❌]
H --> I[Deletes User B's data ❌]
end
subgraph "Fixed Behavior (AFTER FIX)"
E2[取消 Button] --> F2[Clears only visible<br/>100 items ✅]
G2[确认删除 Button] --> H2[Processes only<br/>100 items ✅]
H2 --> I2[Only affects User A ✅]
end
Solution
Fix 1: "取消" Button - Only Uncheck Visible Items
File: src/renderer/src/pages/CleanerPage.tsx:419-432
<button
onClick={() => {
// Only uncheck items that are visible in filteredResults
const visibleCodes = new Set(filteredResults.map((r) => r.materialCode))
setSelectedItems((prev) => {
const newSet = new Set(prev)
for (const code of visibleCodes) {
newSet.delete(code)
}
return newSet
})
}}
className="text-xs bg-white border border-slate-300 text-slate-700 px-2.5 py-1.5 rounded shadow-sm hover:bg-slate-50 flex items-center gap-1"
>
<Square size={14} className="text-slate-400" /> 取消
</button>
What Changed:
- Before:
setSelectedItems(new Set())- clears everything - After: Iterates through
filteredResultsand removes only visible items fromselectedItems - Preserves selections for items not currently visible (e.g., other users' data)
Fix 2: handleConfirmDeletion - Only Process Visible Items (Non-Admin)
File: src/renderer/src/pages/CleanerPage.tsx:158-222
const handleConfirmDeletion = async () => {
// For non-admin users, only process visible filtered results
// For admin users, process all validation results
const resultsToProcess = isAdmin ? validationResults : filteredResults
if (resultsToProcess.length === 0) return alert('没有可处理的数据')
const materialsToUpsert: { materialCode: string; managerName: string }[] = []
const materialsToDelete: string[] = []
const missingManager: string[] = []
for (const result of resultsToProcess) {
// ... rest of processing logic
}
// ...
}
What Changed:
- Before:
for (const result of validationResults)- processes all 1000 items - After:
for (const result of resultsToProcess)where:Admin→ processesvalidationResults(all items)User→ processes onlyfilteredResults(visible items)
Testing Scenarios
Scenario 1: User Unchecks Own Data Only
Setup:
- User A logs in (non-Admin)
- 100 materials visible (assigned to User A)
- 900 materials invisible (assigned to other users)
- All 1000 materials are initially checked
Actions:
- User A clicks "取消"
- Table shows all checkboxes unchecked
Expected:
- ✅ User A's 100 materials are unchecked
- ✅ Other users' 900 materials remain checked (not affected)
Verification:
// Before fix: selectedItems.size === 0
// After fix: selectedItems.size === 900 (other users' items still checked)
Scenario 2: User Confirms Deletion
Setup:
- User A logs in (non-Admin)
- User A unchecks 50 of their 100 materials
- 50 items checked (User A's)
- 900 items checked (other users')
Actions:
- User A clicks "确认删除"
- Confirm dialog shows: "写入/更新 50 条记录"
Expected:
- ✅ Only User A's 50 materials are upserted to database
- ✅ Other users' 900 materials are NOT touched
- ✅ No materials are deleted (since other users' items aren't processed)
Scenario 3: Admin Behavior Unchanged
Setup:
- Admin logs in
- All 1000 materials visible
- All filtered by selected managers
Actions:
- Admin clicks "取消" → all visible items unchecked
- Admin clicks "确认删除" → processes all filtered items
Expected:
- ✅ Admin behavior unchanged (can manage all data)
- ✅ Admin can still filter by managers and process filtered results
Security & Scope Implications
Before Fix (Vulnerability)
flowchart LR
UserA[User A] --> Sees[Sees 100 items]
UserB[User B] --> Sees2[Sees 900 items]
Sees --> Clicks[Clicks 取消 + 确认删除]
Clicks --> Deletes[Deletes ALL 1000 items ❌]
Deletes --> Impact[User B loses data ❌]
After Fix (Secure)
flowchart LR
UserA[User A] --> Sees[Sees 100 items]
UserB[User B] --> Sees2[Sees 900 items]
Sees --> Clicks[Clicks 取消 + 确认删除]
Clicks --> Deletes[Deletes 100 items ✅]
Sees2 --> Independent[User B's data independent ✅]
Deletes --> Safe[User scope isolation ✅]
Code Changes Summary
File: src/renderer/src/pages/CleanerPage.tsx
| Line | Change | Description |
|---|---|---|
| 419-432 | Modified "取消" button | Only uncheck visible filteredResults |
| 158-222 | Modified handleConfirmDeletion |
Use resultsToProcess based on isAdmin |
Variables Used
validationResults: All materials from backend (1000 items)filteredResults: Materials after user/manager filtering (100 items for User A)selectedItems: Set of checked material codesisAdmin: Boolean, true for Admin userscurrentUsername: Current logged-in username
Verification Steps
-
Test as User A:
# Login as user1 npm run dev # Navigate to CleanerPage # Verify only user1's materials are visible # Click "取消" → only visible items unchecked # Check selectedItems size = other users' checked items -
Test as User B:
# Login as user2 # Verify user1's changes didn't affect user2's data # All user2's materials should still be intact -
Test as Admin:
# Login as admin # Verify can still see and manage all materials # "取消" and "确认删除" work on all filtered results
Related Files
- Implementation:
src/renderer/src/pages/CleanerPage.tsx - Related:
src/main/ipc/validation-handler.ts(backend matching logic) - Related:
docs/user-override-match-feature.md(user override matching)
Future Improvements
- Add Confirmation Dialog for Scope: Show user how many items will be affected
- Add Audit Logging: Log which user modified which materials
- Add Warning for Large Operations: Warn if user is about to delete many items
- Backend Validation: Add backend check to prevent cross-user data modification
Document End