wekan/docs/Security/PerUserDataAudit2025-12-23/CURRENT_STATUS.md
Lauri Ojansivu a039bb1066
Some checks are pending
Docker / build (push) Waiting to run
Docker Image CI / build (push) Waiting to run
Release Charts / release (push) Waiting to run
Test suite / Meteor tests (push) Waiting to run
Test suite / Coverage report (push) Blocked by required conditions
Per-User and Board-level data save fixes. Part 3.
Thanks to xet7 !
2025-12-23 09:03:41 +02:00

323 lines
9.1 KiB
Markdown

# Per-User Data Audit - Current Status Summary
**Last Updated**: 2025-12-23
**Status**: ✅ Architecture Finalized
**Scope**: All data persistence related to swimlanes, lists, cards, checklists, checklistItems
---
## Key Decision: Data Classification
The system now enforces clear separation:
### ✅ Per-Board Data (MongoDB Documents)
Stored in swimlane/list/card/checklist/checklistItem documents. **All users see the same value.**
| Entity | Properties | Where Stored |
|--------|-----------|-------------|
| Swimlane | title, color, height, sort, archived | swimlanes.js document |
| List | title, color, width, sort, archived, wipLimit, starred | lists.js document |
| Card | title, color, description, swimlaneId, listId, sort, archived | cards.js document |
| Checklist | title, sort, hideCheckedItems, hideAllItems | checklists.js document |
| ChecklistItem | title, sort, isFinished | checklistItems.js document |
### 🔒 Per-User Data (User Profile + Cookies)
Stored in user.profile or cookies. **Each user has their own value, not visible to others.**
| Entity | Properties | Where Stored |
|--------|-----------|-------------|
| User | collapsedSwimlanes | user.profile.collapsedSwimlanes[boardId][swimlaneId] |
| User | collapsedLists | user.profile.collapsedLists[boardId][listId] |
| User | hideMiniCardLabelText | user.profile.hideMiniCardLabelText[boardId] |
| Public User | collapsedSwimlanes | Cookie: wekan-collapsed-swimlanes |
| Public User | collapsedLists | Cookie: wekan-collapsed-lists |
---
## Changes Implemented ✅
### 1. Schema Changes (swimlanes.js, lists.js) ✅ DONE
**Swimlanes**: Added `height` field
```javascript
height: {
type: Number,
optional: true,
defaultValue: -1, // -1 = auto-height, 50-2000 = fixed
custom() {
const h = this.value;
if (h !== -1 && (h < 50 || h > 2000)) {
return 'heightOutOfRange';
}
},
}
```
**Lists**: Added `width` field
```javascript
width: {
type: Number,
optional: true,
defaultValue: 272, // 100-1000 pixels
custom() {
const w = this.value;
if (w < 100 || w > 1000) {
return 'widthOutOfRange';
}
},
}
```
**Status**: ✅ Implemented in swimlanes.js and lists.js
### 2. Card Position Storage (cards.js) ✅ ALREADY CORRECT
Cards already store position per-board:
- `sort` field: decimal number determining order (shared)
- `swimlaneId`: which swimlane (shared)
- `listId`: which list (shared)
**Status**: ✅ No changes needed
### 3. Checklist Position Storage (checklists.js) ✅ ALREADY CORRECT
Checklists already store position per-board:
- `sort` field: decimal number determining order (shared)
- `hideCheckedChecklistItems`: per-board setting
- `hideAllChecklistItems`: per-board setting
**Status**: ✅ No changes needed
### 4. ChecklistItem Position Storage (checklistItems.js) ✅ ALREADY CORRECT
ChecklistItems already store position per-board:
- `sort` field: decimal number determining order (shared)
**Status**: ✅ No changes needed
---
## Changes Not Yet Implemented
### 1. User Model Refactoring (users.js) ⏳ TODO
**Current State**: Users.js still has per-user width/height methods that read from user.profile:
- `getListWidth(boardId, listId)` - reads user.profile.listWidths
- `getSwimlaneHeight(boardId, swimlaneId)` - reads user.profile.swimlaneHeights
- `setListWidth(boardId, listId, width)` - writes to user.profile.listWidths
- `setSwimlaneHeight(boardId, swimlaneId, height)` - writes to user.profile.swimlaneHeights
**Required Change**:
- Remove per-user width/height storage from user.profile
- Refactor methods to read from list/swimlane documents instead
- Remove from user schema definition
**Status**: ⏳ Pending - See IMPLEMENTATION_GUIDE.md for details
### 2. Migration Script ⏳ TODO
**Current State**: No migration exists to move existing per-user data to per-board
**Required**:
- Create `server/migrations/migrateToPerBoardStorage.js`
- Migrate user.profile.swimlaneHeights → swimlane.height
- Migrate user.profile.listWidths → list.width
- Remove old fields from user profiles
- Track migration status
**Status**: ⏳ Pending - Template available in IMPLEMENTATION_GUIDE.md
---
## Data Examples
### Before (Mixed Per-User/Per-Board - WRONG)
```javascript
// Swimlane document (per-board)
{
_id: 'swim123',
title: 'Development',
boardId: 'board123',
// height stored in user profile (per-user) - WRONG!
}
// User A profile (per-user)
{
_id: 'userA',
profile: {
swimlaneHeights: {
'board123': {
'swim123': 300 // Only User A sees 300px height
}
}
}
}
// User B profile (per-user)
{
_id: 'userB',
profile: {
swimlaneHeights: {
'board123': {
'swim123': 400 // Only User B sees 400px height
}
}
}
}
```
### After (Correct Per-Board/Per-User Separation)
```javascript
// Swimlane document (per-board - ALL USERS SEE THIS)
{
_id: 'swim123',
title: 'Development',
boardId: 'board123',
height: 300 // All users see 300px height
}
// User A profile (per-user - only User A's preferences)
{
_id: 'userA',
profile: {
collapsedSwimlanes: {
'board123': {
'swim123': false // User A: swimlane not collapsed
}
},
collapsedLists: { ... },
hideMiniCardLabelText: { ... }
// height and width REMOVED - now in documents
}
}
// User B profile (per-user - only User B's preferences)
{
_id: 'userB',
profile: {
collapsedSwimlanes: {
'board123': {
'swim123': true // User B: swimlane is collapsed
}
},
collapsedLists: { ... },
hideMiniCardLabelText: { ... }
// height and width REMOVED - now in documents
}
}
```
---
## Testing Evidence Required
### Before Starting UI Integration
1. **Schema Validation**
- [ ] Swimlane with height = -1 → accepts
- [ ] Swimlane with height = 100 → accepts
- [ ] Swimlane with height = 25 → rejects (< 50)
- [ ] Swimlane with height = 3000 rejects (> 2000)
2. **Data Retrieval**
- [ ] `Swimlanes.findOne('swim123').height` returns correct value
- [ ] `Lists.findOne('list456').width` returns correct value
- [ ] Default values used when not set
3. **Data Updates**
- [ ] `Swimlanes.update('swim123', { $set: { height: 500 } })` succeeds
- [ ] `Lists.update('list456', { $set: { width: 400 } })` succeeds
4. **Per-User Isolation**
- [ ] User A collapses swimlane → User B's collapse status unchanged
- [ ] User A hides labels → User B's visibility unchanged
---
## Integration Path
### Phase 1: ✅ Schema Definition (DONE)
- Added `height` to Swimlanes
- Added `width` to Lists
- Both with validation (custom functions)
### Phase 2: ⏳ User Model Refactoring (NEXT)
- Update user methods to read from documents
- Remove per-user storage from user.profile
- Create migration script
### Phase 3: ⏳ UI Integration (AFTER Phase 2)
- Update client code to use new storage locations
- Update Meteor methods to update documents
- Update subscriptions if needed
### Phase 4: ⏳ Testing & Deployment (FINAL)
- Run automated tests
- Manual testing with multiple users
- Deploy with data migration
---
## Backward Compatibility
### For Existing Installations
- Old `user.profile.swimlaneHeights` data will be preserved until migration
- Old `user.profile.listWidths` data will be preserved until migration
- New code can read from either location during transition
- Migration script handles moving data safely
### For New Installations
- Only per-board storage will be used
- User.profile will only contain per-user settings
- No legacy data to migrate
---
## File Reference
| Document | Purpose |
|----------|---------|
| [DATA_PERSISTENCE_ARCHITECTURE.md](DATA_PERSISTENCE_ARCHITECTURE.md) | Complete architecture specification |
| [IMPLEMENTATION_GUIDE.md](IMPLEMENTATION_GUIDE.md) | Step-by-step implementation instructions |
| [models/swimlanes.js](../../../models/swimlanes.js) | Swimlane model with new height field |
| [models/lists.js](../../../models/lists.js) | List model with new width field |
---
## Quick Reference: What Changed?
### New Behavior
- **Swimlane Height**: Now stored in swimlane document (per-board)
- **List Width**: Now stored in list document (per-board)
- **Card Positions**: Always been in card document (per-board) ✅
- **Collapse States**: Remain in user.profile (per-user) ✅
- **Label Visibility**: Remains in user.profile (per-user) ✅
### Old Behavior (Being Removed)
- ❌ Swimlane Height: Was in user.profile (per-user)
- ❌ List Width: Was in user.profile (per-user)
### No Change (Already Correct)
- ✅ Card Positions: In card document (per-board)
- ✅ Checklist Positions: In checklist document (per-board)
- ✅ Collapse States: In user.profile (per-user)
---
## Success Criteria
After all phases complete:
1. ✅ All swimlane heights stored in swimlane documents
2. ✅ All list widths stored in list documents
3. ✅ All positions stored in swimlane/list/card/checklist/checklistItem documents
4. ✅ Only collapse states and label visibility in user profiles
5. ✅ No duplicate storage of widths/heights
6. ✅ All users see same dimensions for swimlanes/lists
7. ✅ Each user has independent collapse preferences
8. ✅ Data validates against range constraints
---
**Status**: ✅ Phase 1 Complete, Awaiting Phase 2