Fixed permissions issues

Created default user role
modified server.js to check if roles of admin/user is present
modified server.js to check dashboard cards
set up default dashboard cards to show
This commit is contained in:
Muhammad Ibrahim
2025-09-24 01:56:02 +01:00
parent db0ba201a4
commit 3a0b564a6f
16 changed files with 797 additions and 77 deletions

View File

@@ -20,3 +20,6 @@ AGENT_RATE_LIMIT_MAX=1000
# Logging
LOG_LEVEL=info
ENABLE_LOGGING=true
# User Registration
DEFAULT_USER_ROLE=user

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "settings" ADD COLUMN "default_user_role" TEXT NOT NULL DEFAULT 'user';

View File

@@ -0,0 +1,65 @@
-- Initialize default dashboard preferences for all existing users
-- This migration ensures that all users have proper role-based dashboard preferences
-- Function to create default dashboard preferences for a user
CREATE OR REPLACE FUNCTION init_user_dashboard_preferences(user_id TEXT, user_role TEXT)
RETURNS VOID AS $$
DECLARE
pref_record RECORD;
BEGIN
-- Delete any existing preferences for this user
DELETE FROM dashboard_preferences WHERE dashboard_preferences.user_id = init_user_dashboard_preferences.user_id;
-- Insert role-based preferences
IF user_role = 'admin' THEN
-- Admin gets full access to all cards (iby's preferred layout)
INSERT INTO dashboard_preferences (id, user_id, card_id, enabled, "order", created_at, updated_at)
VALUES
(gen_random_uuid(), user_id, 'totalHosts', true, 0, NOW(), NOW()),
(gen_random_uuid(), user_id, 'hostsNeedingUpdates', true, 1, NOW(), NOW()),
(gen_random_uuid(), user_id, 'totalOutdatedPackages', true, 2, NOW(), NOW()),
(gen_random_uuid(), user_id, 'securityUpdates', true, 3, NOW(), NOW()),
(gen_random_uuid(), user_id, 'totalHostGroups', true, 4, NOW(), NOW()),
(gen_random_uuid(), user_id, 'upToDateHosts', true, 5, NOW(), NOW()),
(gen_random_uuid(), user_id, 'totalRepos', true, 6, NOW(), NOW()),
(gen_random_uuid(), user_id, 'totalUsers', true, 7, NOW(), NOW()),
(gen_random_uuid(), user_id, 'osDistribution', true, 8, NOW(), NOW()),
(gen_random_uuid(), user_id, 'osDistributionBar', true, 9, NOW(), NOW()),
(gen_random_uuid(), user_id, 'recentCollection', true, 10, NOW(), NOW()),
(gen_random_uuid(), user_id, 'updateStatus', true, 11, NOW(), NOW()),
(gen_random_uuid(), user_id, 'packagePriority', true, 12, NOW(), NOW()),
(gen_random_uuid(), user_id, 'recentUsers', true, 13, NOW(), NOW()),
(gen_random_uuid(), user_id, 'quickStats', true, 14, NOW(), NOW());
ELSE
-- Regular users get comprehensive layout but without user management cards
INSERT INTO dashboard_preferences (id, user_id, card_id, enabled, "order", created_at, updated_at)
VALUES
(gen_random_uuid(), user_id, 'totalHosts', true, 0, NOW(), NOW()),
(gen_random_uuid(), user_id, 'hostsNeedingUpdates', true, 1, NOW(), NOW()),
(gen_random_uuid(), user_id, 'totalOutdatedPackages', true, 2, NOW(), NOW()),
(gen_random_uuid(), user_id, 'securityUpdates', true, 3, NOW(), NOW()),
(gen_random_uuid(), user_id, 'totalHostGroups', true, 4, NOW(), NOW()),
(gen_random_uuid(), user_id, 'upToDateHosts', true, 5, NOW(), NOW()),
(gen_random_uuid(), user_id, 'totalRepos', true, 6, NOW(), NOW()),
(gen_random_uuid(), user_id, 'osDistribution', true, 7, NOW(), NOW()),
(gen_random_uuid(), user_id, 'osDistributionBar', true, 8, NOW(), NOW()),
(gen_random_uuid(), user_id, 'recentCollection', true, 9, NOW(), NOW()),
(gen_random_uuid(), user_id, 'updateStatus', true, 10, NOW(), NOW()),
(gen_random_uuid(), user_id, 'packagePriority', true, 11, NOW(), NOW()),
(gen_random_uuid(), user_id, 'quickStats', true, 12, NOW(), NOW());
END IF;
END;
$$ LANGUAGE plpgsql;
-- Apply default preferences to all existing users
DO $$
DECLARE
user_record RECORD;
BEGIN
FOR user_record IN SELECT id, role FROM users LOOP
PERFORM init_user_dashboard_preferences(user_record.id, user_record.role);
END LOOP;
END $$;
-- Drop the temporary function
DROP FUNCTION init_user_dashboard_preferences(TEXT, TEXT);

View File

@@ -168,6 +168,7 @@ model settings {
latest_version String?
update_available Boolean @default(false)
signup_enabled Boolean @default(false)
default_user_role String @default("user")
}
model update_history {

View File

@@ -6,6 +6,7 @@ const { body, validationResult } = require('express-validator');
const { authenticateToken, requireAdmin } = require('../middleware/auth');
const { requireViewUsers, requireManageUsers } = require('../middleware/permissions');
const { v4: uuidv4 } = require('uuid');
const { createDefaultDashboardPreferences } = require('./dashboardPreferencesRoutes');
const router = express.Router();
const prisma = new PrismaClient();
@@ -32,6 +33,8 @@ router.get('/check-admin-users', async (req, res) => {
// Create first admin user (for first-time setup)
router.post('/setup-admin', [
body('firstName').isLength({ min: 1 }).withMessage('First name is required'),
body('lastName').isLength({ min: 1 }).withMessage('Last name is required'),
body('username').isLength({ min: 1 }).withMessage('Username is required'),
body('email').isEmail().withMessage('Valid email is required'),
body('password').isLength({ min: 8 }).withMessage('Password must be at least 8 characters for security')
@@ -45,7 +48,7 @@ router.post('/setup-admin', [
});
}
const { username, email, password } = req.body;
const { firstName, lastName, username, email, password } = req.body;
// Check if any admin users already exist
const adminCount = await prisma.users.count({
@@ -84,6 +87,8 @@ router.post('/setup-admin', [
username: username.trim(),
email: email.trim(),
password_hash: passwordHash,
first_name: firstName.trim(),
last_name: lastName.trim(),
role: 'admin',
is_active: true,
created_at: new Date(),
@@ -98,6 +103,9 @@ router.post('/setup-admin', [
}
});
// Create default dashboard preferences for the new admin user
await createDefaultDashboardPreferences(user.id, 'admin');
res.status(201).json({
message: 'Admin user created successfully',
user: user
@@ -173,7 +181,14 @@ router.post('/admin/users', authenticateToken, requireManageUsers, [
return res.status(400).json({ errors: errors.array() });
}
const { username, email, password, first_name, last_name, role = 'user' } = req.body;
const { username, email, password, first_name, last_name, role } = req.body;
// Get default user role from settings if no role specified
let userRole = role;
if (!userRole) {
const settings = await prisma.settings.findFirst();
userRole = settings?.default_user_role || 'user';
}
// Check if user already exists
const existingUser = await prisma.users.findFirst({
@@ -201,7 +216,7 @@ router.post('/admin/users', authenticateToken, requireManageUsers, [
password_hash: passwordHash,
first_name: first_name || null,
last_name: last_name || null,
role,
role: userRole,
updated_at: new Date()
},
select: {
@@ -216,6 +231,9 @@ router.post('/admin/users', authenticateToken, requireManageUsers, [
}
});
// Create default dashboard preferences for the new user
await createDefaultDashboardPreferences(user.id, userRole);
res.status(201).json({
message: 'User created successfully',
user
@@ -449,6 +467,8 @@ router.get('/signup-enabled', async (req, res) => {
// Public signup endpoint
router.post('/signup', [
body('firstName').isLength({ min: 1 }).withMessage('First name is required'),
body('lastName').isLength({ min: 1 }).withMessage('Last name is required'),
body('username').isLength({ min: 3 }).withMessage('Username must be at least 3 characters'),
body('email').isEmail().withMessage('Valid email is required'),
body('password').isLength({ min: 6 }).withMessage('Password must be at least 6 characters')
@@ -465,7 +485,7 @@ router.post('/signup', [
return res.status(400).json({ errors: errors.array() });
}
const { username, email, password } = req.body;
const { firstName, lastName, username, email, password } = req.body;
// Check if user already exists
const existingUser = await prisma.users.findFirst({
@@ -484,14 +504,19 @@ router.post('/signup', [
// Hash password
const passwordHash = await bcrypt.hash(password, 12);
// Create user with default 'user' role
// Get default user role from settings or environment variable
const defaultRole = settings?.default_user_role || process.env.DEFAULT_USER_ROLE || 'user';
// Create user with default role from settings
const user = await prisma.users.create({
data: {
id: uuidv4(),
username,
email,
password_hash: passwordHash,
role: 'user',
first_name: firstName.trim(),
last_name: lastName.trim(),
role: defaultRole,
updated_at: new Date()
},
select: {
@@ -504,6 +529,9 @@ router.post('/signup', [
}
});
// Create default dashboard preferences for the new user
await createDefaultDashboardPreferences(user.id, defaultRole);
console.log(`New user registered: ${user.username} (${user.email})`);
// Generate token for immediate login

View File

@@ -2,10 +2,115 @@ const express = require('express');
const { body, validationResult } = require('express-validator');
const { PrismaClient } = require('@prisma/client');
const { authenticateToken } = require('../middleware/auth');
const { v4: uuidv4 } = require('uuid');
const router = express.Router();
const prisma = new PrismaClient();
// Helper function to get user permissions based on role
async function getUserPermissions(userRole) {
try {
const permissions = await prisma.role_permissions.findUnique({
where: { role: userRole }
});
// If no specific permissions found, return default admin permissions (for backward compatibility)
if (!permissions) {
console.warn(`No permissions found for role: ${userRole}, defaulting to admin access`);
return {
can_view_dashboard: true,
can_view_hosts: true,
can_manage_hosts: true,
can_view_packages: true,
can_manage_packages: true,
can_view_users: true,
can_manage_users: true,
can_view_reports: true,
can_export_data: true,
can_manage_settings: true
};
}
return permissions;
} catch (error) {
console.error('Error fetching user permissions:', error);
// Return admin permissions as fallback
return {
can_view_dashboard: true,
can_view_hosts: true,
can_manage_hosts: true,
can_view_packages: true,
can_manage_packages: true,
can_view_users: true,
can_manage_users: true,
can_view_reports: true,
can_export_data: true,
can_manage_settings: true
};
}
}
// Helper function to create permission-based dashboard preferences for a new user
async function createDefaultDashboardPreferences(userId, userRole = 'user') {
try {
// Get user's actual permissions
const permissions = await getUserPermissions(userRole);
// Define all possible dashboard cards with their required permissions
const allCards = [
// Host-related cards
{ cardId: 'totalHosts', requiredPermission: 'can_view_hosts', order: 0 },
{ cardId: 'hostsNeedingUpdates', requiredPermission: 'can_view_hosts', order: 1 },
{ cardId: 'upToDateHosts', requiredPermission: 'can_view_hosts', order: 2 },
{ cardId: 'totalHostGroups', requiredPermission: 'can_view_hosts', order: 3 },
// Package-related cards
{ cardId: 'totalOutdatedPackages', requiredPermission: 'can_view_packages', order: 4 },
{ cardId: 'securityUpdates', requiredPermission: 'can_view_packages', order: 5 },
{ cardId: 'packagePriority', requiredPermission: 'can_view_packages', order: 6 },
// Repository-related cards
{ cardId: 'totalRepos', requiredPermission: 'can_view_hosts', order: 7 }, // Repos are host-related
// User management cards (admin only)
{ cardId: 'totalUsers', requiredPermission: 'can_view_users', order: 8 },
{ cardId: 'recentUsers', requiredPermission: 'can_view_users', order: 9 },
// System/Report cards
{ cardId: 'osDistribution', requiredPermission: 'can_view_reports', order: 10 },
{ cardId: 'osDistributionBar', requiredPermission: 'can_view_reports', order: 11 },
{ cardId: 'updateStatus', requiredPermission: 'can_view_reports', order: 12 },
{ cardId: 'recentCollection', requiredPermission: 'can_view_hosts', order: 13 }, // Collection is host-related
{ cardId: 'quickStats', requiredPermission: 'can_view_dashboard', order: 14 }
];
// Filter cards based on user's permissions
const allowedCards = allCards.filter(card => {
return permissions[card.requiredPermission] === true;
});
// Create preferences data
const preferencesData = allowedCards.map((card) => ({
id: uuidv4(),
user_id: userId,
card_id: card.cardId,
enabled: true,
order: card.order, // Preserve original order from allCards
created_at: new Date(),
updated_at: new Date()
}));
await prisma.dashboard_preferences.createMany({
data: preferencesData
});
console.log(`Permission-based dashboard preferences created for user ${userId} with role ${userRole}: ${allowedCards.length} cards`);
} catch (error) {
console.error('Error creating default dashboard preferences:', error);
// Don't throw error - this shouldn't break user creation
}
}
// Get user's dashboard preferences
router.get('/', authenticateToken, async (req, res) => {
try {
@@ -69,22 +174,24 @@ router.put('/', authenticateToken, [
// Get default dashboard card configuration
router.get('/defaults', authenticateToken, async (req, res) => {
try {
// Default configuration based on iby's (Muhammad Ibrahim) preferred layout
// This provides a comprehensive dashboard view for all new users
const defaultCards = [
{ cardId: 'totalHosts', title: 'Total Hosts', icon: 'Server', enabled: true, order: 0 },
{ cardId: 'hostsNeedingUpdates', title: 'Needs Updating', icon: 'AlertTriangle', enabled: true, order: 1 },
{ cardId: 'totalOutdatedPackages', title: 'Outdated Packages', icon: 'Package', enabled: true, order: 2 },
{ cardId: 'securityUpdates', title: 'Security Updates', icon: 'Shield', enabled: true, order: 3 },
{ cardId: 'upToDateHosts', title: 'Up to date', icon: 'CheckCircle', enabled: true, order: 4 },
{ cardId: 'totalHostGroups', title: 'Host Groups', icon: 'Folder', enabled: false, order: 5 },
{ cardId: 'totalUsers', title: 'Users', icon: 'Users', enabled: false, order: 6 },
{ cardId: 'totalRepos', title: 'Repositories', icon: 'GitBranch', enabled: false, order: 7 },
{ cardId: 'totalHostGroups', title: 'Host Groups', icon: 'Folder', enabled: true, order: 4 },
{ cardId: 'upToDateHosts', title: 'Up to date', icon: 'CheckCircle', enabled: true, order: 5 },
{ cardId: 'totalRepos', title: 'Repositories', icon: 'GitBranch', enabled: true, order: 6 },
{ cardId: 'totalUsers', title: 'Users', icon: 'Users', enabled: true, order: 7 },
{ cardId: 'osDistribution', title: 'OS Distribution', icon: 'BarChart3', enabled: true, order: 8 },
{ cardId: 'osDistributionBar', title: 'OS Distribution (Bar)', icon: 'BarChart3', enabled: false, order: 9 },
{ cardId: 'updateStatus', title: 'Update Status', icon: 'BarChart3', enabled: true, order: 10 },
{ cardId: 'packagePriority', title: 'Package Priority', icon: 'BarChart3', enabled: true, order: 11 },
{ cardId: 'quickStats', title: 'Quick Stats', icon: 'TrendingUp', enabled: true, order: 12 },
{ cardId: 'osDistributionBar', title: 'OS Distribution (Bar)', icon: 'BarChart3', enabled: true, order: 9 },
{ cardId: 'recentCollection', title: 'Recent Collection', icon: 'Server', enabled: true, order: 10 },
{ cardId: 'updateStatus', title: 'Update Status', icon: 'BarChart3', enabled: true, order: 11 },
{ cardId: 'packagePriority', title: 'Package Priority', icon: 'BarChart3', enabled: true, order: 12 },
{ cardId: 'recentUsers', title: 'Recent Users Logged in', icon: 'Users', enabled: true, order: 13 },
{ cardId: 'recentCollection', title: 'Recent Collection', icon: 'Server', enabled: true, order: 14 }
{ cardId: 'quickStats', title: 'Quick Stats', icon: 'TrendingUp', enabled: true, order: 14 }
];
res.json(defaultCards);
@@ -94,4 +201,4 @@ router.get('/defaults', authenticateToken, async (req, res) => {
}
});
module.exports = router;
module.exports = { router, createDefaultDashboardPreferences };

View File

@@ -59,9 +59,9 @@ router.put('/roles/:role', authenticateToken, requireManageSettings, async (req,
can_manage_settings
} = req.body;
// Prevent modifying admin role permissions (admin should always have full access)
if (role === 'admin') {
return res.status(400).json({ error: 'Cannot modify admin role permissions' });
// Prevent modifying admin and user role permissions (built-in roles)
if (role === 'admin' || role === 'user') {
return res.status(400).json({ error: `Cannot modify ${role} role permissions - this is a built-in role` });
}
const permissions = await prisma.role_permissions.upsert({
@@ -111,9 +111,9 @@ router.delete('/roles/:role', authenticateToken, requireManageSettings, async (r
try {
const { role } = req.params;
// Prevent deleting admin role
if (role === 'admin') {
return res.status(400).json({ error: 'Cannot delete admin role' });
// Prevent deleting admin and user roles (built-in roles)
if (role === 'admin' || role === 'user') {
return res.status(400).json({ error: `Cannot delete ${role} role - this is a built-in role` });
}
// Check if any users are using this role

View File

@@ -111,6 +111,7 @@ router.put('/', authenticateToken, requireManageSettings, [
body('updateInterval').isInt({ min: 5, max: 1440 }).withMessage('Update interval must be between 5 and 1440 minutes'),
body('autoUpdate').isBoolean().withMessage('Auto update must be a boolean'),
body('signupEnabled').isBoolean().withMessage('Signup enabled must be a boolean'),
body('defaultUserRole').optional().isLength({ min: 1 }).withMessage('Default user role must be a non-empty string'),
body('githubRepoUrl').optional().isLength({ min: 1 }).withMessage('GitHub repo URL must be a non-empty string'),
body('repositoryType').optional().isIn(['public', 'private']).withMessage('Repository type must be public or private'),
body('sshKeyPath').optional().custom((value) => {
@@ -130,7 +131,7 @@ router.put('/', authenticateToken, requireManageSettings, [
return res.status(400).json({ errors: errors.array() });
}
const { serverProtocol, serverHost, serverPort, updateInterval, autoUpdate, signupEnabled, githubRepoUrl, repositoryType, sshKeyPath } = req.body;
const { serverProtocol, serverHost, serverPort, updateInterval, autoUpdate, signupEnabled, defaultUserRole, githubRepoUrl, repositoryType, sshKeyPath } = req.body;
// Get current settings to check for update interval changes
const currentSettings = await getSettings();
@@ -144,6 +145,7 @@ router.put('/', authenticateToken, requireManageSettings, [
update_interval: updateInterval || 60,
auto_update: autoUpdate || false,
signup_enabled: signupEnabled || false,
default_user_role: defaultUserRole || process.env.DEFAULT_USER_ROLE || 'user',
github_repo_url: githubRepoUrl !== undefined ? githubRepoUrl : 'git@github.com:9technologygroup/patchmon.net.git',
repository_type: repositoryType || 'public',
ssh_key_path: sshKeyPath || null,

View File

@@ -14,7 +14,7 @@ const packageRoutes = require('./routes/packageRoutes');
const dashboardRoutes = require('./routes/dashboardRoutes');
const permissionsRoutes = require('./routes/permissionsRoutes');
const settingsRoutes = require('./routes/settingsRoutes');
const dashboardPreferencesRoutes = require('./routes/dashboardPreferencesRoutes');
const { router: dashboardPreferencesRoutes, createDefaultDashboardPreferences } = require('./routes/dashboardPreferencesRoutes');
const repositoryRoutes = require('./routes/repositoryRoutes');
const versionRoutes = require('./routes/versionRoutes');
const tfaRoutes = require('./routes/tfaRoutes');
@@ -188,6 +188,111 @@ async function checkAndImportAgentVersion() {
}
}
// Function to check and create default role permissions on startup
async function checkAndCreateRolePermissions() {
console.log('🔐 Starting role permissions auto-creation check...');
// Skip if auto-creation is disabled
if (process.env.AUTO_CREATE_ROLE_PERMISSIONS === 'false') {
console.log('❌ Auto-creation of role permissions is disabled');
if (process.env.ENABLE_LOGGING === 'true') {
logger.info('Auto-creation of role permissions is disabled');
}
return;
}
try {
const crypto = require('crypto');
// Define default roles and permissions
const defaultRoles = [
{
id: crypto.randomUUID(),
role: 'admin',
can_view_dashboard: true,
can_view_hosts: true,
can_manage_hosts: true,
can_view_packages: true,
can_manage_packages: true,
can_view_users: true,
can_manage_users: true,
can_view_reports: true,
can_export_data: true,
can_manage_settings: true,
created_at: new Date(),
updated_at: new Date()
},
{
id: crypto.randomUUID(),
role: 'user',
can_view_dashboard: true,
can_view_hosts: true,
can_manage_hosts: false,
can_view_packages: true,
can_manage_packages: false,
can_view_users: false,
can_manage_users: false,
can_view_reports: true,
can_export_data: false,
can_manage_settings: false,
created_at: new Date(),
updated_at: new Date()
}
];
const createdRoles = [];
const existingRoles = [];
for (const roleData of defaultRoles) {
// Check if role already exists
const existingRole = await prisma.role_permissions.findUnique({
where: { role: roleData.role }
});
if (existingRole) {
console.log(`✅ Role '${roleData.role}' already exists in database`);
existingRoles.push(existingRole);
if (process.env.ENABLE_LOGGING === 'true') {
logger.info(`Role '${roleData.role}' already exists in database`);
}
} else {
// Create new role permission
const permission = await prisma.role_permissions.create({
data: roleData
});
createdRoles.push(permission);
console.log(`🆕 Created role '${roleData.role}' with permissions`);
if (process.env.ENABLE_LOGGING === 'true') {
logger.info(`Created role '${roleData.role}' with permissions`);
}
}
}
if (createdRoles.length > 0) {
console.log(`🎉 Successfully auto-created ${createdRoles.length} role permissions on startup`);
console.log('📋 Created roles:');
createdRoles.forEach(role => {
console.log(`${role.role}: dashboard=${role.can_view_dashboard}, hosts=${role.can_manage_hosts}, packages=${role.can_manage_packages}, users=${role.can_manage_users}, settings=${role.can_manage_settings}`);
});
if (process.env.ENABLE_LOGGING === 'true') {
logger.info(`✅ Auto-created ${createdRoles.length} role permissions on startup`);
}
} else {
console.log(`✅ All default role permissions already exist (${existingRoles.length} roles verified)`);
if (process.env.ENABLE_LOGGING === 'true') {
logger.info(`All default role permissions already exist (${existingRoles.length} roles verified)`);
}
}
} catch (error) {
console.error('❌ Failed to check/create role permissions on startup:', error.message);
if (process.env.ENABLE_LOGGING === 'true') {
logger.error('Failed to check/create role permissions on startup:', error.message);
}
}
}
// Initialize logger - only if logging is enabled
const logger = process.env.ENABLE_LOGGING === 'true' ? winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
@@ -405,6 +510,201 @@ process.on('SIGTERM', async () => {
process.exit(0);
});
// Initialize dashboard preferences for all users
async function initializeDashboardPreferences() {
try {
console.log('🔧 Initializing dashboard preferences for all users...');
// Get all users
const users = await prisma.users.findMany({
select: {
id: true,
username: true,
email: true,
role: true,
dashboard_preferences: {
select: {
card_id: true
}
}
}
});
if (users.length === 0) {
console.log(' No users found in database');
return;
}
console.log(`📊 Found ${users.length} users to initialize`);
let initializedCount = 0;
let updatedCount = 0;
for (const user of users) {
const hasPreferences = user.dashboard_preferences.length > 0;
// Get permission-based preferences for this user's role
const expectedPreferences = await getPermissionBasedPreferences(user.role);
const expectedCardCount = expectedPreferences.length;
if (!hasPreferences) {
// User has no preferences - create them
console.log(`⚙️ Creating preferences for ${user.username} (${user.role})`);
const preferencesData = expectedPreferences.map(pref => ({
id: require('uuid').v4(),
user_id: user.id,
card_id: pref.cardId,
enabled: pref.enabled,
order: pref.order,
created_at: new Date(),
updated_at: new Date()
}));
await prisma.dashboard_preferences.createMany({
data: preferencesData
});
initializedCount++;
console.log(` ✅ Created ${expectedCardCount} cards based on permissions`);
} else {
// User already has preferences - check if they need updating
const currentCardCount = user.dashboard_preferences.length;
if (currentCardCount !== expectedCardCount) {
console.log(`🔄 Updating preferences for ${user.username} (${user.role}) - ${currentCardCount}${expectedCardCount} cards`);
// Delete existing preferences
await prisma.dashboard_preferences.deleteMany({
where: { user_id: user.id }
});
// Create new preferences based on permissions
const preferencesData = expectedPreferences.map(pref => ({
id: require('uuid').v4(),
user_id: user.id,
card_id: pref.cardId,
enabled: pref.enabled,
order: pref.order,
created_at: new Date(),
updated_at: new Date()
}));
await prisma.dashboard_preferences.createMany({
data: preferencesData
});
updatedCount++;
console.log(` ✅ Updated to ${expectedCardCount} cards based on permissions`);
} else {
console.log(`${user.username} already has correct preferences (${currentCardCount} cards)`);
}
}
}
console.log(`\n📋 Dashboard Preferences Initialization Complete:`);
console.log(` - New users initialized: ${initializedCount}`);
console.log(` - Existing users updated: ${updatedCount}`);
console.log(` - Users with correct preferences: ${users.length - initializedCount - updatedCount}`);
console.log(`\n🎯 Permission-based preferences:`);
console.log(` - Cards are now assigned based on actual user permissions`);
console.log(` - Each card requires specific permissions (can_view_hosts, can_view_users, etc.)`);
console.log(` - Users only see cards they have permission to access`);
} catch (error) {
console.error('❌ Error initializing dashboard preferences:', error);
throw error;
}
}
// Helper function to get user permissions based on role
async function getUserPermissions(userRole) {
try {
const permissions = await prisma.role_permissions.findUnique({
where: { role: userRole }
});
// If no specific permissions found, return default admin permissions (for backward compatibility)
if (!permissions) {
console.warn(`No permissions found for role: ${userRole}, defaulting to admin access`);
return {
can_view_dashboard: true,
can_view_hosts: true,
can_manage_hosts: true,
can_view_packages: true,
can_manage_packages: true,
can_view_users: true,
can_manage_users: true,
can_view_reports: true,
can_export_data: true,
can_manage_settings: true
};
}
return permissions;
} catch (error) {
console.error('Error fetching user permissions:', error);
// Return admin permissions as fallback
return {
can_view_dashboard: true,
can_view_hosts: true,
can_manage_hosts: true,
can_view_packages: true,
can_manage_packages: true,
can_view_users: true,
can_manage_users: true,
can_view_reports: true,
can_export_data: true,
can_manage_settings: true
};
}
}
// Helper function to get permission-based dashboard preferences for a role
async function getPermissionBasedPreferences(userRole) {
// Get user's actual permissions
const permissions = await getUserPermissions(userRole);
// Define all possible dashboard cards with their required permissions
const allCards = [
// Host-related cards
{ cardId: 'totalHosts', requiredPermission: 'can_view_hosts', order: 0 },
{ cardId: 'hostsNeedingUpdates', requiredPermission: 'can_view_hosts', order: 1 },
{ cardId: 'upToDateHosts', requiredPermission: 'can_view_hosts', order: 2 },
{ cardId: 'totalHostGroups', requiredPermission: 'can_view_hosts', order: 3 },
// Package-related cards
{ cardId: 'totalOutdatedPackages', requiredPermission: 'can_view_packages', order: 4 },
{ cardId: 'securityUpdates', requiredPermission: 'can_view_packages', order: 5 },
{ cardId: 'packagePriority', requiredPermission: 'can_view_packages', order: 6 },
// Repository-related cards
{ cardId: 'totalRepos', requiredPermission: 'can_view_hosts', order: 7 }, // Repos are host-related
// User management cards (admin only)
{ cardId: 'totalUsers', requiredPermission: 'can_view_users', order: 8 },
{ cardId: 'recentUsers', requiredPermission: 'can_view_users', order: 9 },
// System/Report cards
{ cardId: 'osDistribution', requiredPermission: 'can_view_reports', order: 10 },
{ cardId: 'osDistributionBar', requiredPermission: 'can_view_reports', order: 11 },
{ cardId: 'updateStatus', requiredPermission: 'can_view_reports', order: 12 },
{ cardId: 'recentCollection', requiredPermission: 'can_view_hosts', order: 13 }, // Collection is host-related
{ cardId: 'quickStats', requiredPermission: 'can_view_dashboard', order: 14 }
];
// Filter cards based on user's permissions
const allowedCards = allCards.filter(card => {
return permissions[card.requiredPermission] === true;
});
return allowedCards.map((card) => ({
cardId: card.cardId,
enabled: true,
order: card.order // Preserve original order from allCards
}));
}
// Start server with database health check
async function startServer() {
try {
@@ -430,7 +730,12 @@ async function startServer() {
// Check and import agent version on startup
await checkAndImportAgentVersion();
// Check and create default role permissions on startup
await checkAndCreateRolePermissions();
// Initialize dashboard preferences for all users
await initializeDashboardPreferences();
app.listen(PORT, () => {
if (process.env.ENABLE_LOGGING === 'true') {
logger.info(`Server running on port ${PORT}`);