Files
location-mqtt-tracker-app/scripts/fix-acl-topic-patterns.js
Joachim Hummel 31c0e1f572 Fix MQTT topic pattern for OwnTracks and implement privacy isolation
CRITICAL FIX: The OwnTracks app publishes to owntracks/<username>/<device_id>,
not owntracks/owntrack/<device_id>. This was causing data delivery failures
and privacy violations.

Changes:
- Fix ACL topic pattern: owntracks/<username>/# (was: owntracks/owntrack/<device_id>)
- Backend now uses MQTT_ADMIN_USERNAME for global subscription
- Update UI forms and placeholders with correct pattern
- Update email template with correct topic format
- Enable Mosquitto ACL file for user isolation
- Add migration script for existing ACL rules
- Update documentation (README, GEMINI.md)

Privacy & Security:
- Each user isolated at MQTT broker level via ACL
- Backend subscribes with admin credentials to owntracks/+/+
- Web UI filters data by parent_user_id for additional security
- GDPR compliant multi-layer defense in depth

Files changed:
- lib/mqtt-db.ts - Updated createDefaultRule() to use username
- app/api/mqtt/credentials/route.ts - Pass username to ACL creation
- app/admin/mqtt/page.tsx - UI forms and state management
- emails/mqtt-credentials.tsx - Email template topic pattern
- lib/mqtt-subscriber.ts - Use admin credentials from env
- mosquitto/config/mosquitto.conf - Enable ACL enforcement
- README.md, GEMINI.md - Documentation updates
- scripts/fix-acl-topic-patterns.js - Migration script
- MQTT_TOPIC_FIX.md - Detailed implementation guide

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 21:49:31 +00:00

89 lines
2.3 KiB
JavaScript

#!/usr/bin/env node
/**
* Migration script to fix ACL topic patterns
*
* Changes: owntracks/owntrack/<device_id> → owntracks/<username>/#
*
* This script:
* 1. Finds all ACL rules with the old pattern
* 2. Looks up the correct MQTT username for each device
* 3. Updates the topic_pattern to use the username
*/
const Database = require('better-sqlite3');
const path = require('path');
const dbPath = path.join(__dirname, '..', 'tracker.db');
const db = new Database(dbPath);
console.log('🔧 Fixing ACL topic patterns...\n');
try {
// Get all ACL rules with the old pattern
const aclRules = db.prepare(`
SELECT id, device_id, topic_pattern, permission
FROM mqtt_acl_rules
WHERE topic_pattern LIKE 'owntracks/owntrack/%'
`).all();
console.log(`Found ${aclRules.length} ACL rules to fix\n`);
if (aclRules.length === 0) {
console.log('✓ No ACL rules need fixing!');
db.close();
process.exit(0);
}
let fixed = 0;
let failed = 0;
for (const rule of aclRules) {
// Look up the MQTT username for this device
const credential = db.prepare(`
SELECT mqtt_username
FROM mqtt_credentials
WHERE device_id = ?
`).get(rule.device_id);
if (!credential) {
console.log(`⚠ Warning: No MQTT credentials found for device ${rule.device_id}, skipping...`);
failed++;
continue;
}
const oldPattern = rule.topic_pattern;
const newPattern = `owntracks/${credential.mqtt_username}/#`;
// Update the ACL rule
db.prepare(`
UPDATE mqtt_acl_rules
SET topic_pattern = ?
WHERE id = ?
`).run(newPattern, rule.id);
console.log(`✓ Fixed rule for device ${rule.device_id}:`);
console.log(` Old: ${oldPattern}`);
console.log(` New: ${newPattern}\n`);
fixed++;
}
// Mark pending changes for MQTT sync
db.prepare(`
UPDATE mqtt_sync_status
SET pending_changes = pending_changes + 1
`).run();
console.log('─'.repeat(50));
console.log(`\n✅ Migration complete!`);
console.log(` Fixed: ${fixed}`);
console.log(` Failed: ${failed}`);
console.log(`\n⚠️ Run MQTT Sync to apply changes to Mosquitto broker`);
} catch (error) {
console.error('❌ Error during migration:', error);
process.exit(1);
} finally {
db.close();
}