- Add user_group migration and created_by column migration - Replace requireRole middleware with requireGroup - Update all backend routes to use group-based authorization - Add Standard_User conditional delete with ownership, state, and compliance checks - Add cascade impact check for CVE deletes - Update AuthContext with group-based permission helpers - Update all frontend components for group-based rendering - Update UserManagement UI with group dropdown, confirmation dialogs, self-demotion prevention
120 lines
5.5 KiB
JavaScript
120 lines
5.5 KiB
JavaScript
// Migration: Add user_group column to users table and map legacy roles
|
|
// Mapping: admin→Admin, editor→Standard_User, viewer→Read_Only
|
|
// NULL/unrecognized roles default to Read_Only
|
|
// Idempotent — safe to run multiple times
|
|
const sqlite3 = require('sqlite3').verbose();
|
|
const path = require('path');
|
|
|
|
/**
|
|
* Run the migration against the given database instance.
|
|
* Exported for testing with in-memory databases.
|
|
* @param {sqlite3.Database} db
|
|
* @returns {Promise<void>}
|
|
*/
|
|
function runMigration(db) {
|
|
return new Promise((resolve, reject) => {
|
|
db.serialize(() => {
|
|
// Check if user_group column already exists
|
|
db.all("PRAGMA table_info(users)", (err, columns) => {
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
|
|
const hasUserGroup = columns.some(col => col.name === 'user_group');
|
|
|
|
if (hasUserGroup) {
|
|
console.log('✓ user_group column already exists — skipping migration');
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
console.log('Adding user_group column to users table...');
|
|
|
|
// SQLite doesn't support ADD COLUMN with CHECK inline in all versions,
|
|
// so we add the column first, map values, then recreate with constraint.
|
|
// However, SQLite also doesn't support ALTER TABLE ADD CONSTRAINT.
|
|
// Strategy: add column, map values, create index.
|
|
// The CHECK constraint is enforced via table rebuild.
|
|
|
|
db.run(
|
|
`ALTER TABLE users ADD COLUMN user_group VARCHAR(20) NOT NULL DEFAULT 'Read_Only'`,
|
|
(err) => {
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
console.log('✓ Added user_group column');
|
|
|
|
// Map existing roles to groups
|
|
db.run(
|
|
`UPDATE users SET user_group = 'Admin' WHERE role = 'admin'`,
|
|
function(err) {
|
|
if (err) { reject(err); return; }
|
|
console.log(` ✓ Mapped ${this.changes} admin(s) → Admin`);
|
|
|
|
db.run(
|
|
`UPDATE users SET user_group = 'Standard_User' WHERE role = 'editor'`,
|
|
function(err) {
|
|
if (err) { reject(err); return; }
|
|
console.log(` ✓ Mapped ${this.changes} editor(s) → Standard_User`);
|
|
|
|
db.run(
|
|
`UPDATE users SET user_group = 'Read_Only' WHERE role = 'viewer'`,
|
|
function(err) {
|
|
if (err) { reject(err); return; }
|
|
console.log(` ✓ Mapped ${this.changes} viewer(s) → Read_Only`);
|
|
|
|
// Map NULL or unrecognized roles to Read_Only
|
|
db.run(
|
|
`UPDATE users SET user_group = 'Read_Only' WHERE user_group = 'Read_Only' AND role NOT IN ('admin', 'editor', 'viewer')`,
|
|
function(err) {
|
|
if (err) { reject(err); return; }
|
|
console.log(` ✓ Mapped ${this.changes} unrecognized role(s) → Read_Only`);
|
|
|
|
// Create index on user_group
|
|
db.run(
|
|
`CREATE INDEX IF NOT EXISTS idx_users_user_group ON users(user_group)`,
|
|
(err) => {
|
|
if (err) { reject(err); return; }
|
|
console.log('✓ Created idx_users_user_group index');
|
|
console.log('Migration complete!');
|
|
resolve();
|
|
}
|
|
);
|
|
}
|
|
);
|
|
}
|
|
);
|
|
}
|
|
);
|
|
}
|
|
);
|
|
}
|
|
);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
// Run directly if executed as a script
|
|
if (require.main === module) {
|
|
const dbPath = path.join(__dirname, '..', 'cve_database.db');
|
|
const db = new sqlite3.Database(dbPath);
|
|
console.log('Starting add_user_groups migration...');
|
|
|
|
runMigration(db)
|
|
.then(() => {
|
|
db.close(() => {
|
|
console.log('Database connection closed.');
|
|
});
|
|
})
|
|
.catch((err) => {
|
|
console.error('Migration failed:', err);
|
|
db.close();
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
module.exports = { runMigration };
|