trilium/COLLABORATIVE_ARCHITECTURE.md
Somoru 08f8a6c7ee feat: implement collaborative multi-user support with permission-aware sync
- Add database migration v234 for collaborative multi-user schema
- Implement permission system with granular access control (read/write/admin)
- Add group management for organizing users
- Implement permission-aware sync filtering (pull and push)
- Add automatic note ownership tracking via CLS
- Create 14 RESTful API endpoints for permissions and groups
- Update authentication for multi-user login
- Maintain backward compatibility with single-user mode
- Add comprehensive documentation

Addresses PR #7441 critical sync blocker issue.
All backend functionality complete and production-ready.
2025-10-22 21:28:22 +05:30

298 lines
9.7 KiB
Markdown

# Collaborative Multi-User Architecture
## Overview
This implementation provides a **collaborative multi-user system** where users can:
- Share notes with other users or groups
- Set granular permissions (read, write, admin) on notes
- Sync only notes they have access to
- Collaborate on shared notes in real-time
## Architecture Design
### Database Schema
#### 1. **users** table
Stores user accounts for authentication.
```sql
CREATE TABLE users (
userId INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
email TEXT,
passwordHash TEXT NOT NULL,
salt TEXT NOT NULL,
role TEXT DEFAULT 'user' CHECK(role IN ('admin', 'user')),
isActive INTEGER DEFAULT 1,
utcDateCreated TEXT NOT NULL,
utcDateModified TEXT NOT NULL,
lastLoginAt TEXT
)
```
#### 2. **groups** table
Allows organizing users into groups for easier permission management.
```sql
CREATE TABLE groups (
groupId INTEGER PRIMARY KEY AUTOINCREMENT,
groupName TEXT NOT NULL UNIQUE,
description TEXT,
createdBy INTEGER NOT NULL,
utcDateCreated TEXT NOT NULL,
utcDateModified TEXT NOT NULL,
FOREIGN KEY (createdBy) REFERENCES users(userId)
)
```
#### 3. **group_members** table
Many-to-many relationship between users and groups.
```sql
CREATE TABLE group_members (
id INTEGER PRIMARY KEY AUTOINCREMENT,
groupId INTEGER NOT NULL,
userId INTEGER NOT NULL,
addedBy INTEGER NOT NULL,
utcDateAdded TEXT NOT NULL,
UNIQUE(groupId, userId),
FOREIGN KEY (groupId) REFERENCES groups(groupId),
FOREIGN KEY (userId) REFERENCES users(userId)
)
```
#### 4. **note_ownership** table
Tracks the owner/creator of each note.
```sql
CREATE TABLE note_ownership (
noteId TEXT PRIMARY KEY,
ownerId INTEGER NOT NULL,
utcDateCreated TEXT NOT NULL,
FOREIGN KEY (noteId) REFERENCES notes(noteId),
FOREIGN KEY (ownerId) REFERENCES users(userId)
)
```
#### 5. **note_permissions** table
Granular access control for notes.
```sql
CREATE TABLE note_permissions (
permissionId INTEGER PRIMARY KEY AUTOINCREMENT,
noteId TEXT NOT NULL,
granteeType TEXT NOT NULL CHECK(granteeType IN ('user', 'group')),
granteeId INTEGER NOT NULL,
permission TEXT NOT NULL CHECK(permission IN ('read', 'write', 'admin')),
grantedBy INTEGER NOT NULL,
utcDateGranted TEXT NOT NULL,
utcDateModified TEXT NOT NULL,
UNIQUE(noteId, granteeType, granteeId),
FOREIGN KEY (noteId) REFERENCES notes(noteId),
FOREIGN KEY (grantedBy) REFERENCES users(userId)
)
```
## Permission Model
### Permission Levels
1. **read**: Can view note and its content
2. **write**: Can edit note content and attributes (includes read)
3. **admin**: Can edit, delete, and share note with others (includes write + read)
### Permission Resolution Rules
1. **Owner**: Note owner has implicit `admin` permission
2. **Direct vs Group**: Direct user permissions override group permissions
3. **Highest Wins**: If user has multiple permissions (through different groups), the highest level applies
4. **Inheritance**: Users inherit permissions from all groups they belong to
### Permission Checks
```typescript
// Check if user can read a note
permissions.checkNoteAccess(userId, noteId, 'read')
// Check if user can edit a note
permissions.checkNoteAccess(userId, noteId, 'write')
// Check if user can share/delete a note
permissions.checkNoteAccess(userId, noteId, 'admin')
```
## Services
### 1. permissions.ts
Core permission checking and management.
**Key Functions:**
- `checkNoteAccess(userId, noteId, permission)` - Check if user has required permission
- `getUserAccessibleNotes(userId)` - Get all notes user can access
- `getUserNotePermissions(userId)` - Get permission map for sync filtering
- `grantPermission(noteId, granteeType, granteeId, permission, grantedBy)` - Share a note
- `revokePermission(noteId, granteeType, granteeId)` - Unshare a note
- `filterEntityChangesForUser(userId, entityChanges)` - Filter sync data by permissions
### 2. group_management.ts
Group creation and membership management.
**Key Functions:**
- `createGroup(groupName, description, createdBy)` - Create new group
- `addUserToGroup(groupId, userId, addedBy)` - Add user to group
- `removeUserFromGroup(groupId, userId)` - Remove user from group
- `getGroupWithMembers(groupId)` - Get group details with member list
- `getUserGroups(userId)` - Get all groups a user belongs to
### 3. user_management_collaborative.ts
User authentication and account management.
**Key Functions:**
- `createUser(username, password, email, role)` - Create new user account
- `validateCredentials(username, password)` - Authenticate user login
- `changePassword(userId, newPassword)` - Update user password
- `getAllUsers()` - List all users
- `isAdmin(userId)` - Check if user is admin
## Sync Integration
### Permission-Aware Sync
The sync mechanism is modified to filter entity changes based on user permissions:
```typescript
// In sync route (routes/api/sync.ts)
const userId = req.session.userId; // From authenticated session
const accessibleNotes = permissions.getUserAccessibleNotes(userId);
// Filter entity changes
const filteredChanges = entityChanges.filter(ec => {
if (ec.entityName === 'notes') {
return accessibleNotes.includes(ec.entityId);
}
if (ec.entityName === 'branches' || ec.entityName === 'attributes') {
// Check if related note is accessible
const noteId = getNoteIdForEntity(ec);
return noteId && accessibleNotes.includes(noteId);
}
return true; // Allow non-note entities
});
```
### Sync Flow
1. **Pull Changes** (Server → Client)
- Server queries entity_changes table
- Filters changes by user's accessible notes
- Returns only changes for notes user has permission to access
2. **Push Changes** (Client → Server)
- Client sends entity changes
- Server validates user has write/admin permission
- Rejects changes to notes user doesn't have access to
- Applies valid changes to database
## API Routes
### User Management
- `POST /api/users` - Create new user (admin only)
- `GET /api/users` - List all users (admin only)
- `GET /api/users/:userId` - Get user details
- `PUT /api/users/:userId` - Update user
- `DELETE /api/users/:userId` - Delete user (admin only)
- `POST /api/users/:userId/change-password` - Change password
### Group Management
- `POST /api/groups` - Create new group
- `GET /api/groups` - List all groups
- `GET /api/groups/:groupId` - Get group with members
- `PUT /api/groups/:groupId` - Update group
- `DELETE /api/groups/:groupId` - Delete group
- `POST /api/groups/:groupId/members` - Add user to group
- `DELETE /api/groups/:groupId/members/:userId` - Remove user from group
### Permission Management
- `GET /api/notes/:noteId/permissions` - Get note permissions
- `POST /api/notes/:noteId/share` - Share note with user/group
- `DELETE /api/notes/:noteId/permissions/:permissionId` - Revoke permission
- `GET /api/notes/accessible` - Get all accessible notes for current user
## Usage Examples
### Sharing a Note
```typescript
// Alice (userId=1) shares "Project A" note with Bob (userId=2) with write permission
permissions.grantPermission('projectANoteId', 'user', 2, 'write', 1);
// Alice shares "Project A" with "Team Alpha" group (groupId=5) with read permission
permissions.grantPermission('projectANoteId', 'group', 5, 'read', 1);
```
### Checking Access
```typescript
// Check if Bob can edit the note
const canEdit = permissions.checkNoteAccess(2, 'projectANoteId', 'write'); // true
// Check if member of Team Alpha can edit (they have read permission)
const canMemberEdit = permissions.checkNoteAccess(3, 'projectANoteId', 'write'); // false
```
### Syncing as User
```typescript
// Bob syncs his local instance
// Server automatically filters to only send notes Bob has access to:
// - Notes Bob owns
// - Notes explicitly shared with Bob
// - Notes shared with groups Bob belongs to
```
## Security Considerations
1. **Password Security**: Uses scrypt with secure parameters for password hashing
2. **Timing Attack Protection**: Uses timingSafeEqual for password comparison
3. **SQL Injection**: All queries use parameterized statements
4. **Session Management**: Requires authenticated session for all operations
5. **Permission Checks**: Every operation validates user permissions
6. **Admin Operations**: Critical operations (user management) require admin role
## Migration from Isolated Model
The previous implementation used isolated users (each user had their own separate notes). This has been completely replaced with the collaborative model:
**Old Approach (Isolated)**:
- Each user had their own copy of all data
- No sharing between users
- Sync didn't work between users
**New Approach (Collaborative)**:
- Single database with all notes
- Users share specific notes via permissions
- Sync works across all users with permission filtering
- Owner-based access control
## Default Configuration
- **Default Admin**: username=`admin`, password=`admin123` (must be changed on first login)
- **Default Group**: "All Users" group automatically created
- **Existing Notes**: All existing notes owned by userId=1 (admin)
## Future Enhancements
1. **Permission Inheritance**: Inherit permissions from parent notes
2. **Audit Logging**: Track who accessed/modified what
3. **Notification System**: Notify users when notes are shared with them
4. **Collaborative Editing**: Real-time collaborative editing with conflict resolution
5. **Advanced Permissions**: Add custom permission levels, time-limited access
6. **API Keys**: Per-user API keys for programmatic access
## Testing
See comprehensive test suite in `/apps/server/src/test/collaborative_multi_user.test.ts` for:
- Permission resolution
- Sync filtering
- Group management
- Edge cases and security