Implement RBAC migration and role management enhancements
- Added AJAX functionality to fetch role permissions for copying. - Introduced system role management with permission checks for updates. - Implemented role deletion with confirmation modal and backend handling. - Enhanced user role assignment migration scripts to transition from legacy profiles to RBAC. - Created SQL migration scripts for user roles and permissions mapping. - Updated user interface to support new role management features including copy permissions and system role indicators.
This commit is contained in:
BIN
assets/.DS_Store
vendored
BIN
assets/.DS_Store
vendored
Binary file not shown.
222
assets/database/migration_profiles_to_rbac.sql
Normal file
222
assets/database/migration_profiles_to_rbac.sql
Normal file
@@ -0,0 +1,222 @@
|
||||
-- ===================================================
|
||||
-- PROFILE TO RBAC MIGRATION SCRIPT
|
||||
-- Date: 2025-01-22
|
||||
-- Description: Migrate from settingsprofiles.php to user_roles RBAC system
|
||||
-- Note: Uses existing access_elements table (already populated)
|
||||
-- ===================================================
|
||||
|
||||
START TRANSACTION;
|
||||
|
||||
-- ===================================================
|
||||
-- PHASE 1: CREATE ROLES (matching existing profiles)
|
||||
-- ===================================================
|
||||
|
||||
INSERT INTO `user_roles` (`name`, `description`, `is_active`, `created`, `createdby`) VALUES
|
||||
('Standard', 'Basic user access - view equipment, history, service reports', 1, NOW(), 1),
|
||||
('Superuser', 'Extended access - manage equipment, products, users', 1, NOW(), 1),
|
||||
('Admin', 'Administrative access - full management capabilities', 1, NOW(), 1),
|
||||
('AdminPlus', 'System administrator - complete system access', 1, NOW(), 1),
|
||||
('Build', 'Build tool access only', 1, NOW(), 1),
|
||||
('Commerce', 'E-commerce and catalog management', 1, NOW(), 1),
|
||||
('Distribution', 'Distribution partner access', 1, NOW(), 1),
|
||||
('Firmware', 'Firmware/software update access only', 1, NOW(), 1),
|
||||
('Garage', 'Car testing and diagnostics', 1, NOW(), 1),
|
||||
('Interface', 'API/Interface access', 1, NOW(), 1),
|
||||
('Service', 'Service technician access', 1, NOW(), 1),
|
||||
('Other', 'Miscellaneous access level', 1, NOW(), 1)
|
||||
ON DUPLICATE KEY UPDATE `description` = VALUES(`description`);
|
||||
|
||||
-- ===================================================
|
||||
-- PHASE 2: CREATE ROLE_ACCESS_PERMISSIONS MAPPINGS
|
||||
-- ===================================================
|
||||
|
||||
-- Get role IDs
|
||||
SET @role_standard = (SELECT rowID FROM user_roles WHERE name = 'Standard' LIMIT 1);
|
||||
SET @role_superuser = (SELECT rowID FROM user_roles WHERE name = 'Superuser' LIMIT 1);
|
||||
SET @role_admin = (SELECT rowID FROM user_roles WHERE name = 'Admin' LIMIT 1);
|
||||
SET @role_adminplus = (SELECT rowID FROM user_roles WHERE name = 'AdminPlus' LIMIT 1);
|
||||
SET @role_build = (SELECT rowID FROM user_roles WHERE name = 'Build' LIMIT 1);
|
||||
SET @role_commerce = (SELECT rowID FROM user_roles WHERE name = 'Commerce' LIMIT 1);
|
||||
SET @role_distribution = (SELECT rowID FROM user_roles WHERE name = 'Distribution' LIMIT 1);
|
||||
SET @role_firmware = (SELECT rowID FROM user_roles WHERE name = 'Firmware' LIMIT 1);
|
||||
SET @role_garage = (SELECT rowID FROM user_roles WHERE name = 'Garage' LIMIT 1);
|
||||
SET @role_interface = (SELECT rowID FROM user_roles WHERE name = 'Interface' LIMIT 1);
|
||||
SET @role_service = (SELECT rowID FROM user_roles WHERE name = 'Service' LIMIT 1);
|
||||
SET @role_other = (SELECT rowID FROM user_roles WHERE name = 'Other' LIMIT 1);
|
||||
|
||||
-- ===================================================
|
||||
-- STANDARD ROLE PERMISSIONS (Read-only)
|
||||
-- Profile: application,firmwaretool,histories,history,servicereport,servicereports,dashboard,profile,equipment,equipments,products_software
|
||||
-- ===================================================
|
||||
INSERT INTO `role_access_permissions` (`role_id`, `access_id`, `can_create`, `can_read`, `can_update`, `can_delete`)
|
||||
SELECT @role_standard, rowID, 0, 1, 0, 0 FROM access_elements WHERE access_path IN (
|
||||
'application', 'firmwaretool', 'histories', 'history', 'servicereport', 'servicereports',
|
||||
'dashboard', 'profile', 'equipment', 'equipments', 'products_software'
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE can_read = 1;
|
||||
|
||||
-- ===================================================
|
||||
-- SUPERUSER ROLE PERMISSIONS (Create, Read, Update)
|
||||
-- Profile: application,assets,firmwaretool,histories,history,history_manage,marketing,partner,partners,
|
||||
-- servicereport,servicereports,admin,dashboard,profile,equipment,equipment_manage,
|
||||
-- equipment_manage_edit,equipments,equipments_mass_update,product,product_manage,products,
|
||||
-- products_software,products_versions,user,user_manage,users
|
||||
-- ===================================================
|
||||
INSERT INTO `role_access_permissions` (`role_id`, `access_id`, `can_create`, `can_read`, `can_update`, `can_delete`)
|
||||
SELECT @role_superuser, rowID, 1, 1, 1, 0 FROM access_elements WHERE access_path IN (
|
||||
'application', 'firmwaretool', 'histories', 'history', 'history_manage',
|
||||
'marketing', 'partner', 'partners', 'servicereport', 'servicereports',
|
||||
'dashboard', 'profile', 'equipment', 'equipment_manage',
|
||||
'equipments', 'equipments_mass_update', 'product', 'product_manage', 'products',
|
||||
'products_software', 'products_versions', 'user', 'users'
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE can_create = 1, can_read = 1, can_update = 1;
|
||||
|
||||
-- ===================================================
|
||||
-- ADMIN ROLE PERMISSIONS (Full CRUD)
|
||||
-- ===================================================
|
||||
INSERT INTO `role_access_permissions` (`role_id`, `access_id`, `can_create`, `can_read`, `can_update`, `can_delete`)
|
||||
SELECT @role_admin, rowID, 1, 1, 1, 1 FROM access_elements WHERE access_path IN (
|
||||
'application', 'buildtool', 'cartest', 'cartest_manage', 'cartests',
|
||||
'changelog', 'communication', 'communication_send', 'communications', 'firmwaretool',
|
||||
'histories', 'history', 'history_manage', 'marketing', 'partner', 'partners',
|
||||
'servicereport', 'servicereports', 'software_available', 'software_download',
|
||||
'software_update', 'softwaretool', 'account', 'accounts', 'dashboard', 'profile',
|
||||
'contract', 'contract_manage', 'contracts', 'equipment', 'equipment_data',
|
||||
'equipment_healthindex', 'equipment_history', 'equipment_manage',
|
||||
'equipments', 'equipments_mass_update', 'product', 'product_manage', 'products',
|
||||
'products_software', 'products_software_assignment', 'products_software_assignments',
|
||||
'products_software_licenses', 'products_versions', 'report_build',
|
||||
'report_contracts_billing', 'report_healthindex', 'rma', 'rma_history',
|
||||
'rma_manage', 'rmas', 'user', 'users'
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE can_create = 1, can_read = 1, can_update = 1, can_delete = 1;
|
||||
|
||||
-- ===================================================
|
||||
-- ADMINPLUS ROLE PERMISSIONS (Full access to everything)
|
||||
-- ===================================================
|
||||
INSERT INTO `role_access_permissions` (`role_id`, `access_id`, `can_create`, `can_read`, `can_update`, `can_delete`)
|
||||
SELECT @role_adminplus, rowID, 1, 1, 1, 1 FROM access_elements WHERE is_active = 1
|
||||
ON DUPLICATE KEY UPDATE can_create = 1, can_read = 1, can_update = 1, can_delete = 1;
|
||||
|
||||
-- ===================================================
|
||||
-- BUILD ROLE PERMISSIONS
|
||||
-- Profile: application,buildtool,firmwaretool,dashboard,profile,products_software
|
||||
-- ===================================================
|
||||
INSERT INTO `role_access_permissions` (`role_id`, `access_id`, `can_create`, `can_read`, `can_update`, `can_delete`)
|
||||
SELECT @role_build, rowID, 1, 1, 1, 0 FROM access_elements WHERE access_path IN (
|
||||
'application', 'buildtool', 'firmwaretool', 'dashboard', 'profile', 'products_software'
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE can_create = 1, can_read = 1, can_update = 1;
|
||||
|
||||
-- ===================================================
|
||||
-- COMMERCE ROLE PERMISSIONS
|
||||
-- ===================================================
|
||||
INSERT INTO `role_access_permissions` (`role_id`, `access_id`, `can_create`, `can_read`, `can_update`, `can_delete`)
|
||||
SELECT @role_commerce, rowID, 1, 1, 1, 1 FROM access_elements WHERE access_path IN (
|
||||
'application', 'catalog', 'categories', 'category', 'checkout', 'discount', 'discounts',
|
||||
'identity', 'invoice', 'media', 'media_manage', 'order', 'orders', 'partner', 'partners',
|
||||
'placeorder', 'pricelists', 'pricelists_items', 'pricelists_manage', 'shipping',
|
||||
'shipping_manage', 'shopping_cart', 'taxes', 'transactions', 'transactions_items',
|
||||
'translation_manage', 'translations', 'translations_details', 'uploader',
|
||||
'dashboard', 'profile', 'product', 'product_manage', 'products', 'products_attributes',
|
||||
'products_attributes_items', 'products_attributes_manage', 'products_categories',
|
||||
'products_configurations', 'products_media', 'products_software', 'products_versions',
|
||||
'user', 'users'
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE can_create = 1, can_read = 1, can_update = 1, can_delete = 1;
|
||||
|
||||
-- ===================================================
|
||||
-- DISTRIBUTION ROLE PERMISSIONS
|
||||
-- ===================================================
|
||||
INSERT INTO `role_access_permissions` (`role_id`, `access_id`, `can_create`, `can_read`, `can_update`, `can_delete`)
|
||||
SELECT @role_distribution, rowID, 1, 1, 1, 0 FROM access_elements WHERE access_path IN (
|
||||
'application', 'firmwaretool', 'histories', 'history', 'history_manage',
|
||||
'marketing', 'partner', 'partners', 'servicereport', 'servicereports',
|
||||
'dashboard', 'profile', 'equipment', 'equipment_manage',
|
||||
'equipments', 'equipments_mass_update', 'product', 'product_manage', 'products',
|
||||
'products_software', 'products_versions', 'user', 'users'
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE can_create = 1, can_read = 1, can_update = 1;
|
||||
|
||||
-- ===================================================
|
||||
-- FIRMWARE ROLE PERMISSIONS
|
||||
-- Profile: application,software_available,software_download,software_update,softwaretool,
|
||||
-- transactions,transactions_items,products_software_versions
|
||||
-- ===================================================
|
||||
INSERT INTO `role_access_permissions` (`role_id`, `access_id`, `can_create`, `can_read`, `can_update`, `can_delete`)
|
||||
SELECT @role_firmware, rowID, 0, 1, 1, 0 FROM access_elements WHERE access_path IN (
|
||||
'application', 'software_available', 'software_download', 'software_update',
|
||||
'softwaretool', 'transactions', 'transactions_items', 'products_software_versions'
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE can_read = 1, can_update = 1;
|
||||
|
||||
-- ===================================================
|
||||
-- GARAGE ROLE PERMISSIONS
|
||||
-- Profile: application,cartest,cartest_manage,cartests,dashboard,profile,products_versions
|
||||
-- ===================================================
|
||||
INSERT INTO `role_access_permissions` (`role_id`, `access_id`, `can_create`, `can_read`, `can_update`, `can_delete`)
|
||||
SELECT @role_garage, rowID, 1, 1, 1, 0 FROM access_elements WHERE access_path IN (
|
||||
'application', 'cartest', 'cartest_manage', 'cartests', 'dashboard', 'profile', 'products_versions'
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE can_create = 1, can_read = 1, can_update = 1;
|
||||
|
||||
-- ===================================================
|
||||
-- INTERFACE ROLE PERMISSIONS
|
||||
-- Profile: application,firmwaretool,invoice,payment,transactions,transactions_items,
|
||||
-- contract,contracts,equipment_manage,equipments,products_software,products_versions,users
|
||||
-- ===================================================
|
||||
INSERT INTO `role_access_permissions` (`role_id`, `access_id`, `can_create`, `can_read`, `can_update`, `can_delete`)
|
||||
SELECT @role_interface, rowID, 1, 1, 1, 0 FROM access_elements WHERE access_path IN (
|
||||
'application', 'firmwaretool', 'invoice', 'payment', 'transactions', 'transactions_items',
|
||||
'contract', 'contracts', 'equipment_manage', 'equipments', 'products_software',
|
||||
'products_versions', 'users'
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE can_create = 1, can_read = 1, can_update = 1;
|
||||
|
||||
-- ===================================================
|
||||
-- SERVICE ROLE PERMISSIONS
|
||||
-- Profile: application,assets,firmwaretool,histories,history,history_manage,marketing,partner,partners,
|
||||
-- servicereport,servicereports,admin,dashboard,profile,equipment,equipment_manage,equipments,
|
||||
-- products_software,user,user_manage,users
|
||||
-- ===================================================
|
||||
INSERT INTO `role_access_permissions` (`role_id`, `access_id`, `can_create`, `can_read`, `can_update`, `can_delete`)
|
||||
SELECT @role_service, rowID, 1, 1, 1, 0 FROM access_elements WHERE access_path IN (
|
||||
'application', 'firmwaretool', 'histories', 'history', 'history_manage',
|
||||
'marketing', 'partner', 'partners', 'servicereport', 'servicereports',
|
||||
'dashboard', 'profile', 'equipment', 'equipment_manage', 'equipments', 'products_software',
|
||||
'user', 'users'
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE can_create = 1, can_read = 1, can_update = 1;
|
||||
|
||||
-- ===================================================
|
||||
-- OTHER ROLE PERMISSIONS
|
||||
-- Profile: application,assets,firmwaretool,histories,history,history_manage,marketing,partner,partners,
|
||||
-- servicereport,servicereports,admin,dashboard,profile,equipment,equipment_manage,equipments,products_software
|
||||
-- ===================================================
|
||||
INSERT INTO `role_access_permissions` (`role_id`, `access_id`, `can_create`, `can_read`, `can_update`, `can_delete`)
|
||||
SELECT @role_other, rowID, 0, 1, 1, 0 FROM access_elements WHERE access_path IN (
|
||||
'application', 'firmwaretool', 'histories', 'history', 'history_manage',
|
||||
'marketing', 'partner', 'partners', 'servicereport', 'servicereports',
|
||||
'dashboard', 'profile', 'equipment', 'equipment_manage', 'equipments', 'products_software'
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE can_read = 1, can_update = 1;
|
||||
|
||||
-- ===================================================
|
||||
-- VERIFICATION QUERIES
|
||||
-- ===================================================
|
||||
|
||||
-- Check roles created
|
||||
SELECT rowID, name, description, is_active FROM user_roles ORDER BY rowID;
|
||||
|
||||
-- Check permissions per role
|
||||
SELECT ur.name as role_name, COUNT(rap.rowID) as permission_count
|
||||
FROM user_roles ur
|
||||
LEFT JOIN role_access_permissions rap ON ur.rowID = rap.role_id
|
||||
GROUP BY ur.rowID, ur.name
|
||||
ORDER BY ur.rowID;
|
||||
|
||||
-- ===================================================
|
||||
-- Change ROLLBACK to COMMIT when ready to apply
|
||||
-- ===================================================
|
||||
COMMIT;
|
||||
228
assets/database/migration_users_to_rbac.sql
Normal file
228
assets/database/migration_users_to_rbac.sql
Normal file
@@ -0,0 +1,228 @@
|
||||
-- ===================================================
|
||||
-- USER TO RBAC ROLE ASSIGNMENT MIGRATION SCRIPT
|
||||
-- Date: 2025-01-22
|
||||
-- Description: Migrate users from settings/view fields to user_role_assignments
|
||||
-- Prerequisites: Run migration_profiles_to_rbac.sql first to create roles
|
||||
-- ===================================================
|
||||
|
||||
START TRANSACTION;
|
||||
|
||||
-- ===================================================
|
||||
-- MAPPING REFERENCE:
|
||||
--
|
||||
-- users.settings field values -> role names:
|
||||
-- 'standard_profile' or empty with view 0-2 -> Standard
|
||||
-- 'superuser_profile' or view=2 -> Superuser
|
||||
-- 'admin_profile' or view=4 -> Admin
|
||||
-- 'adminplus_profile' or view=5 -> AdminPlus
|
||||
-- 'build' -> Build
|
||||
-- 'commerce' -> Commerce
|
||||
-- 'distribution' -> Distribution
|
||||
-- 'firmware' -> Firmware
|
||||
-- 'garage' -> Garage
|
||||
-- 'interface' -> Interface
|
||||
-- 'service' -> Service
|
||||
-- 'other' -> Other
|
||||
--
|
||||
-- users.view field (legacy permission level):
|
||||
-- 1 = SuperUser
|
||||
-- 2 = Create & Update
|
||||
-- 3 = Read-only
|
||||
-- 4 = Admin
|
||||
-- 5 = Admin+
|
||||
-- ===================================================
|
||||
|
||||
-- Get role IDs
|
||||
SET @role_standard = (SELECT rowID FROM user_roles WHERE name = 'Standard' LIMIT 1);
|
||||
SET @role_superuser = (SELECT rowID FROM user_roles WHERE name = 'Superuser' LIMIT 1);
|
||||
SET @role_admin = (SELECT rowID FROM user_roles WHERE name = 'Admin' LIMIT 1);
|
||||
SET @role_adminplus = (SELECT rowID FROM user_roles WHERE name = 'AdminPlus' LIMIT 1);
|
||||
SET @role_build = (SELECT rowID FROM user_roles WHERE name = 'Build' LIMIT 1);
|
||||
SET @role_commerce = (SELECT rowID FROM user_roles WHERE name = 'Commerce' LIMIT 1);
|
||||
SET @role_distribution = (SELECT rowID FROM user_roles WHERE name = 'Distribution' LIMIT 1);
|
||||
SET @role_firmware = (SELECT rowID FROM user_roles WHERE name = 'Firmware' LIMIT 1);
|
||||
SET @role_garage = (SELECT rowID FROM user_roles WHERE name = 'Garage' LIMIT 1);
|
||||
SET @role_interface = (SELECT rowID FROM user_roles WHERE name = 'Interface' LIMIT 1);
|
||||
SET @role_service = (SELECT rowID FROM user_roles WHERE name = 'Service' LIMIT 1);
|
||||
SET @role_other = (SELECT rowID FROM user_roles WHERE name = 'Other' LIMIT 1);
|
||||
|
||||
-- ===================================================
|
||||
-- PHASE 1: MIGRATE USERS BY SETTINGS FIELD (profile name)
|
||||
-- ===================================================
|
||||
|
||||
-- Users with 'standard_profile' setting
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT id, @role_standard, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users
|
||||
WHERE settings = 'standard_profile'
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with 'superuser_profile' setting
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT id, @role_superuser, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users
|
||||
WHERE settings = 'superuser_profile'
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with 'admin_profile' setting
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT id, @role_admin, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users
|
||||
WHERE settings = 'admin_profile'
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with 'adminplus_profile' setting
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT id, @role_adminplus, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users
|
||||
WHERE settings = 'adminplus_profile'
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with 'build' setting
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT id, @role_build, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users
|
||||
WHERE settings = 'build'
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with 'commerce' setting
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT id, @role_commerce, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users
|
||||
WHERE settings = 'commerce'
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with 'distribution' setting
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT id, @role_distribution, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users
|
||||
WHERE settings = 'distribution'
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with 'firmware' setting
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT id, @role_firmware, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users
|
||||
WHERE settings = 'firmware'
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with 'garage' setting
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT id, @role_garage, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users
|
||||
WHERE settings = 'garage'
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with 'interface' setting
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT id, @role_interface, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users
|
||||
WHERE settings = 'interface'
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with 'service' setting
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT id, @role_service, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users
|
||||
WHERE settings = 'service'
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with 'other' setting
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT id, @role_other, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users
|
||||
WHERE settings = 'other'
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- ===================================================
|
||||
-- PHASE 2: MIGRATE USERS WITH EMPTY/NULL SETTINGS (use view field)
|
||||
-- Only for users not already assigned a role
|
||||
-- ===================================================
|
||||
|
||||
-- Users with view=5 (Admin+) and no settings
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT u.id, @role_adminplus, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users u
|
||||
LEFT JOIN user_role_assignments ura ON u.id = ura.user_id AND ura.is_active = 1
|
||||
WHERE (u.settings IS NULL OR u.settings = '')
|
||||
AND u.view = '5'
|
||||
AND ura.rowID IS NULL
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with view=4 (Admin) and no settings
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT u.id, @role_admin, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users u
|
||||
LEFT JOIN user_role_assignments ura ON u.id = ura.user_id AND ura.is_active = 1
|
||||
WHERE (u.settings IS NULL OR u.settings = '')
|
||||
AND u.view = '4'
|
||||
AND ura.rowID IS NULL
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with view=1 (SuperUser) and no settings
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT u.id, @role_superuser, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users u
|
||||
LEFT JOIN user_role_assignments ura ON u.id = ura.user_id AND ura.is_active = 1
|
||||
WHERE (u.settings IS NULL OR u.settings = '')
|
||||
AND u.view = '1'
|
||||
AND ura.rowID IS NULL
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- Users with view=2 or view=3 (Create/Update or Read-only) and no settings -> Standard
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT u.id, @role_standard, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users u
|
||||
LEFT JOIN user_role_assignments ura ON u.id = ura.user_id AND ura.is_active = 1
|
||||
WHERE (u.settings IS NULL OR u.settings = '')
|
||||
AND u.view IN ('2', '3')
|
||||
AND ura.rowID IS NULL
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- ===================================================
|
||||
-- PHASE 3: CATCH-ALL - Any remaining users without role -> Standard
|
||||
-- ===================================================
|
||||
|
||||
INSERT INTO `user_role_assignments` (`user_id`, `role_id`, `is_active`, `assigned_by`, `assigned_at`, `created`, `createdby`)
|
||||
SELECT u.id, @role_standard, 1, 'migration_script', NOW(), NOW(), 1
|
||||
FROM users u
|
||||
LEFT JOIN user_role_assignments ura ON u.id = ura.user_id AND ura.is_active = 1
|
||||
WHERE ura.rowID IS NULL
|
||||
ON DUPLICATE KEY UPDATE updated = NOW();
|
||||
|
||||
-- ===================================================
|
||||
-- VERIFICATION QUERIES
|
||||
-- ===================================================
|
||||
|
||||
-- Check migration results: users per role
|
||||
SELECT
|
||||
ur.name as role_name,
|
||||
COUNT(ura.user_id) as user_count
|
||||
FROM user_roles ur
|
||||
LEFT JOIN user_role_assignments ura ON ur.rowID = ura.role_id AND ura.is_active = 1
|
||||
GROUP BY ur.rowID, ur.name
|
||||
ORDER BY user_count DESC;
|
||||
|
||||
-- Check for users without role assignments (should be 0)
|
||||
SELECT COUNT(*) as users_without_role
|
||||
FROM users u
|
||||
LEFT JOIN user_role_assignments ura ON u.id = ura.user_id AND ura.is_active = 1
|
||||
WHERE ura.rowID IS NULL;
|
||||
|
||||
-- Compare old vs new: show users with their old settings and new role
|
||||
SELECT
|
||||
u.id,
|
||||
u.username,
|
||||
u.settings as old_profile,
|
||||
u.view as old_view_level,
|
||||
ur.name as new_role
|
||||
FROM users u
|
||||
LEFT JOIN user_role_assignments ura ON u.id = ura.user_id AND ura.is_active = 1
|
||||
LEFT JOIN user_roles ur ON ura.role_id = ur.rowID
|
||||
ORDER BY u.id
|
||||
LIMIT 50;
|
||||
|
||||
-- ===================================================
|
||||
-- Change ROLLBACK to COMMIT when ready to apply
|
||||
-- ===================================================
|
||||
COMMIT;
|
||||
@@ -515,10 +515,19 @@ echo <<<EOT
|
||||
|
||||
// Intercept form submissions
|
||||
setupFormInterception();
|
||||
|
||||
|
||||
// Intercept fetch and XMLHttpRequest
|
||||
interceptNetworkRequests();
|
||||
|
||||
|
||||
// Intercept form submissions to show loading
|
||||
function setupFormInterception() {
|
||||
document.querySelectorAll('form').forEach(function(form) {
|
||||
form.addEventListener('submit', function() {
|
||||
showLoading();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Intercept all network requests (fetch and XMLHttpRequest)
|
||||
function interceptNetworkRequests() {
|
||||
// Track active requests
|
||||
@@ -1637,42 +1646,25 @@ function getProfile($profile, $permission){
|
||||
|
||||
// Always allowed collections: [collection => allowed_actions_string]
|
||||
$always_allowed = [
|
||||
'com_log' => 'U',
|
||||
'com_log' => 'CRU',
|
||||
'application' => 'CRU',
|
||||
'user_permissions' => 'R',
|
||||
'software_update' => 'R',
|
||||
'software_download' => 'R',
|
||||
'software_available' => 'R',
|
||||
'history' => 'U',
|
||||
'payment' => 'U',
|
||||
'marketing_files' => 'CRUD',
|
||||
'marketing_folders' => 'CRUD',
|
||||
'marketing_tags' => 'CRUD',
|
||||
'marketing_upload' => 'CRUD',
|
||||
'marketing_delete' => 'CRUD'
|
||||
'history' => 'RU',
|
||||
'payment' => 'U'
|
||||
];
|
||||
|
||||
// Debug log - initial call
|
||||
if(debug){
|
||||
$perm_count = is_array($permissions) ? count($permissions) : 'not_array';
|
||||
$test = "$date - isAllowed called: access_element=$access_element, basic_permission_level=$basic_permission_level, action=$action, permissions_count=$perm_count".PHP_EOL;
|
||||
error_log($test, 3, $filelocation);
|
||||
}
|
||||
|
||||
// 1. Check if basic_permission_level is 4 (System-admin+) - always allow
|
||||
if ($basic_permission_level !== null && $basic_permission_level == 4) {
|
||||
if(debug){
|
||||
$test = "$date - Allowed by system permission (level 5)".PHP_EOL;
|
||||
error_log($test, 3, $filelocation);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 2. Check always_allowed list
|
||||
if (isset($always_allowed[$access_element]) && str_contains($always_allowed[$access_element], $action)) {
|
||||
if(debug){
|
||||
$test = "$date - Allowed by always_allowed list".PHP_EOL;
|
||||
error_log($test, 3, $filelocation);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1691,20 +1683,21 @@ function getProfile($profile, $permission){
|
||||
$permission_key = $action_map[$action] ?? null;
|
||||
|
||||
if ($permission_key && isset($element_permissions[$permission_key]) && $element_permissions[$permission_key] == 1) {
|
||||
if(debug){
|
||||
$test = "$date - Allowed by RBAC permissions: $access_element -> $permission_key = 1".PHP_EOL;
|
||||
error_log($test, 3, $filelocation);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(debug){
|
||||
$test = "$date - isAllowed called: access_element=$access_element, basic_permission_level=$basic_permission_level, action=$action".PHP_EOL;
|
||||
error_log($test, 3, $filelocation);
|
||||
$perm_value = $element_permissions[$permission_key] ?? 'not_set';
|
||||
$test = "$date - RBAC check failed: $access_element -> $permission_key = $perm_value".PHP_EOL;
|
||||
error_log($test, 3, $filelocation);
|
||||
}
|
||||
} else {
|
||||
if(debug){
|
||||
$test = "$date - isAllowed called: access_element=$access_element, basic_permission_level=$basic_permission_level, action=$action".PHP_EOL;
|
||||
error_log($test, 3, $filelocation);
|
||||
$test = "$date - Access element '$access_element' not found in permissions array".PHP_EOL;
|
||||
error_log($test, 3, $filelocation);
|
||||
}
|
||||
@@ -1712,9 +1705,12 @@ function getProfile($profile, $permission){
|
||||
|
||||
// Not allowed
|
||||
if(debug){
|
||||
$test = "$date - isAllowed called: access_element=$access_element, basic_permission_level=$basic_permission_level, action=$action".PHP_EOL;
|
||||
error_log($test, 3, $filelocation);
|
||||
$test = "$date - Not allowed: access_element=$access_element, action=$action".PHP_EOL;
|
||||
error_log($test, 3, $filelocation);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3913,27 +3909,29 @@ function dateInRange($start_date, $end_date, $date_check)
|
||||
|
||||
function getLatestVersion($productcode,$token){
|
||||
|
||||
//CALL TO API TO GET ALL ACTIVE CONTRACTS
|
||||
$api_url = '/v2/products_software/productcode='.$productcode;
|
||||
$responses = ioAPIv2($api_url,'',$token);
|
||||
//$pdo = dbConnect($dbname);
|
||||
|
||||
//Decode Payload
|
||||
if (!empty($responses)){$responses = json_decode($responses,true);
|
||||
}
|
||||
else{
|
||||
$responses = $output = array(
|
||||
"productcode" => "",
|
||||
"version"=> "",
|
||||
"mandatory"=> "",
|
||||
"latest"=> "",
|
||||
"software"=> "",
|
||||
"source" => "",
|
||||
"source_type" => ""
|
||||
);
|
||||
;}
|
||||
//CALL TO API TO GET ALL ACTIVE CONTRACTS
|
||||
$api_url = '/v2/products_software/productcode='.$productcode;
|
||||
$responses = ioAPIv2($api_url,'',$token);
|
||||
|
||||
//Decode Payload
|
||||
if (!empty($responses)){$responses = json_decode($responses,true);
|
||||
}
|
||||
else{
|
||||
$responses = $output = array(
|
||||
"productcode" => "",
|
||||
"version"=> "",
|
||||
"mandatory"=> "",
|
||||
"latest"=> "",
|
||||
"software"=> "",
|
||||
"source" => "",
|
||||
"source_type" => ""
|
||||
);
|
||||
;}
|
||||
|
||||
//DEFAULT OUTPUT
|
||||
return $responses;
|
||||
//DEFAULT OUTPUT
|
||||
return $responses;
|
||||
}
|
||||
|
||||
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
@@ -249,15 +249,19 @@ class MarketingFileManager {
|
||||
emptyState.style.display = 'none';
|
||||
|
||||
try {
|
||||
// Use proper folder ID (null for root, or the folder ID)
|
||||
const folderId = this.currentFolder ? this.currentFolder : 'null';
|
||||
// Add cache busting to prevent browser caching
|
||||
let url = `index.php?page=marketing&action=marketing_files&folder_id=${folderId}&limit=50&_t=${Date.now()}`;
|
||||
|
||||
let url = `index.php?page=marketing&action=marketing_files&limit=50&_t=${Date.now()}`;
|
||||
|
||||
// Only filter by folder if no tag filter is active (tag search is across all folders)
|
||||
if (!this.filters.tag) {
|
||||
const folderId = this.currentFolder ? this.currentFolder : 'null';
|
||||
url += `&folder_id=${folderId}`;
|
||||
}
|
||||
|
||||
if (this.filters.search) {
|
||||
url += `&search=${encodeURIComponent(this.filters.search)}`;
|
||||
}
|
||||
|
||||
|
||||
if (this.filters.tag) {
|
||||
url += `&tag=${encodeURIComponent(this.filters.tag)}`;
|
||||
}
|
||||
@@ -289,21 +293,33 @@ class MarketingFileManager {
|
||||
|
||||
if (data && data.length > 0) {
|
||||
let files = data;
|
||||
|
||||
|
||||
// Client-side file type filtering
|
||||
if (this.filters.fileTypes.length > 0) {
|
||||
files = files.filter(file =>
|
||||
files = files.filter(file =>
|
||||
this.filters.fileTypes.includes(file.file_type.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (files.length === 0) {
|
||||
emptyState.style.display = 'block';
|
||||
// No files after filtering, check for subfolders
|
||||
const subfolders = this.getSubfolders(this.currentFolder);
|
||||
if (subfolders.length > 0) {
|
||||
this.renderFolderTiles(subfolders);
|
||||
} else {
|
||||
emptyState.style.display = 'block';
|
||||
}
|
||||
} else {
|
||||
this.renderFiles(files);
|
||||
}
|
||||
} else {
|
||||
emptyState.style.display = 'block';
|
||||
// No files, check for subfolders
|
||||
const subfolders = this.getSubfolders(this.currentFolder);
|
||||
if (subfolders.length > 0) {
|
||||
this.renderFolderTiles(subfolders);
|
||||
} else {
|
||||
emptyState.style.display = 'block';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading files:', error);
|
||||
@@ -372,12 +388,73 @@ class MarketingFileManager {
|
||||
renderFiles(files) {
|
||||
const container = document.getElementById('filesContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
|
||||
files.forEach(file => {
|
||||
const fileElement = this.createFileElement(file);
|
||||
container.appendChild(fileElement);
|
||||
});
|
||||
}
|
||||
|
||||
getSubfolders(folderId) {
|
||||
// Find immediate children of the specified folder
|
||||
if (!folderId || folderId === '') {
|
||||
// Root folder - return top-level folders
|
||||
return this.folders;
|
||||
}
|
||||
|
||||
// Recursively search for the folder and return its children
|
||||
const findFolder = (folders, targetId) => {
|
||||
for (const folder of folders) {
|
||||
if (folder.id === targetId) {
|
||||
return folder.children || [];
|
||||
}
|
||||
if (folder.children && folder.children.length > 0) {
|
||||
const found = findFolder(folder.children, targetId);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
return findFolder(this.folders, folderId);
|
||||
}
|
||||
|
||||
renderFolderTiles(subfolders) {
|
||||
const container = document.getElementById('filesContainer');
|
||||
container.innerHTML = '';
|
||||
|
||||
subfolders.forEach(folder => {
|
||||
const folderElement = this.createFolderTileElement(folder);
|
||||
container.appendChild(folderElement);
|
||||
});
|
||||
}
|
||||
|
||||
createFolderTileElement(folder) {
|
||||
const folderElement = document.createElement('div');
|
||||
folderElement.className = `folder-tile ${this.viewMode}-item`;
|
||||
folderElement.setAttribute('data-folder-id', folder.id);
|
||||
|
||||
folderElement.innerHTML = `
|
||||
<div class="folder-tile-icon">
|
||||
<i class="fa fa-folder"></i>
|
||||
</div>
|
||||
<div class="folder-tile-info">
|
||||
<div class="folder-tile-name" title="${this.escapeHtml(folder.folder_name)}">
|
||||
${this.escapeHtml(folder.folder_name)}
|
||||
</div>
|
||||
<div class="folder-tile-meta">
|
||||
<span class="folder-file-count">${folder.file_count || 0} files</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Click to navigate to folder
|
||||
folderElement.addEventListener('click', () => {
|
||||
this.selectFolder(folder.id);
|
||||
});
|
||||
|
||||
return folderElement;
|
||||
}
|
||||
|
||||
createFileElement(file) {
|
||||
const fileElement = document.createElement('div');
|
||||
@@ -385,7 +462,7 @@ class MarketingFileManager {
|
||||
fileElement.setAttribute('data-file-id', file.id);
|
||||
|
||||
const thumbnail = this.getThumbnail(file);
|
||||
const tags = file.tags.map(tag => `<span class="tag">${this.escapeHtml(tag)}</span>`).join('');
|
||||
const tags = file.tags.map(tag => `<span class="tag clickable" data-tag="${this.escapeHtml(tag)}">${this.escapeHtml(tag)}</span>`).join('');
|
||||
|
||||
fileElement.innerHTML = `
|
||||
<div class="file-thumbnail">
|
||||
@@ -429,11 +506,20 @@ class MarketingFileManager {
|
||||
fileElement.querySelector('.edit-btn').addEventListener('click', () => {
|
||||
this.editFile(file);
|
||||
});
|
||||
|
||||
|
||||
// Make tags clickable to filter by tag
|
||||
fileElement.querySelectorAll('.tag.clickable').forEach(tagElement => {
|
||||
tagElement.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const tagName = tagElement.getAttribute('data-tag');
|
||||
this.filterByTag(tagName);
|
||||
});
|
||||
});
|
||||
|
||||
fileElement.addEventListener('dblclick', () => {
|
||||
this.previewFile(file);
|
||||
});
|
||||
|
||||
|
||||
return fileElement;
|
||||
}
|
||||
|
||||
@@ -843,15 +929,41 @@ class MarketingFileManager {
|
||||
|
||||
updateFileTypeFilters() {
|
||||
this.filters.fileTypes = [];
|
||||
|
||||
|
||||
document.querySelectorAll('.file-type-filters input[type="checkbox"]:checked').forEach(checkbox => {
|
||||
const types = checkbox.value.split(',');
|
||||
this.filters.fileTypes.push(...types);
|
||||
});
|
||||
|
||||
|
||||
this.loadFiles();
|
||||
}
|
||||
|
||||
|
||||
filterByTag(tagName) {
|
||||
// Set the tag filter
|
||||
this.filters.tag = tagName;
|
||||
|
||||
// Update the dropdown to show the selected tag
|
||||
const tagSelect = document.getElementById('tagFilter');
|
||||
if (tagSelect) {
|
||||
tagSelect.value = tagName;
|
||||
}
|
||||
|
||||
// Clear folder selection to search across all folders
|
||||
this.currentFolder = '';
|
||||
|
||||
// Update folder tree UI to show root as active
|
||||
document.querySelectorAll('.folder-item').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
const rootFolder = document.querySelector('.folder-item.root');
|
||||
if (rootFolder) {
|
||||
rootFolder.classList.add('active');
|
||||
}
|
||||
|
||||
// Reload files with the tag filter
|
||||
this.loadFiles();
|
||||
}
|
||||
|
||||
populateTagFilter(tags) {
|
||||
const select = document.getElementById('tagFilter');
|
||||
select.innerHTML = '<option value="">All Tags</option>';
|
||||
|
||||
@@ -100,6 +100,45 @@ if (document.readyState === 'loading') {
|
||||
checkBrowserCompatibility();
|
||||
}
|
||||
|
||||
// Shared serial port reference for upload.js to use
|
||||
window.sharedSerialPort = null;
|
||||
|
||||
// Override requestPort to minimize user prompts
|
||||
// This intercepts all requestPort calls (including from upload.js) to reuse authorized ports
|
||||
if ('serial' in navigator) {
|
||||
const originalRequestPort = navigator.serial.requestPort.bind(navigator.serial);
|
||||
|
||||
navigator.serial.requestPort = async function(options) {
|
||||
// If we have a shared port, return it instead of prompting
|
||||
if (window.sharedSerialPort) {
|
||||
console.log('Using shared serial port (no prompt needed)');
|
||||
return window.sharedSerialPort;
|
||||
}
|
||||
|
||||
// Try already-authorized ports matching the filters
|
||||
const ports = await navigator.serial.getPorts();
|
||||
if (ports.length > 0 && options?.filters) {
|
||||
const match = ports.find(p => {
|
||||
const info = p.getInfo();
|
||||
return options.filters.some(f =>
|
||||
info.usbVendorId === f.usbVendorId &&
|
||||
info.usbProductId === f.usbProductId
|
||||
);
|
||||
});
|
||||
if (match) {
|
||||
console.log('Using previously authorized port (no prompt needed)');
|
||||
window.sharedSerialPort = match;
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: original prompt behavior
|
||||
const port = await originalRequestPort(options);
|
||||
window.sharedSerialPort = port; // Store for future use
|
||||
return port;
|
||||
};
|
||||
}
|
||||
|
||||
// Function to log communication to API (reused from scripts.js)
|
||||
async function logCommunication(data, direction) {
|
||||
// Only log if debug mode is enabled
|
||||
@@ -400,7 +439,11 @@ async function closePortAfterRead() {
|
||||
await port.close();
|
||||
await logCommunication('Port closed successfully', 'info');
|
||||
|
||||
// Reset for next connection
|
||||
// Keep port reference in sharedSerialPort for upload.js to reuse
|
||||
// This prevents the need for another user prompt during firmware upload
|
||||
window.sharedSerialPort = port;
|
||||
|
||||
// Reset local variables for next connection
|
||||
reader = null;
|
||||
writer = null;
|
||||
readableStreamClosed = null;
|
||||
@@ -410,7 +453,12 @@ async function closePortAfterRead() {
|
||||
console.error('Error closing port after read:', error);
|
||||
await logCommunication(`Error closing port: ${error.message}`, 'error');
|
||||
|
||||
// Force reset even on error
|
||||
// Keep port reference even on error if port exists
|
||||
if (port) {
|
||||
window.sharedSerialPort = port;
|
||||
}
|
||||
|
||||
// Force reset local variables even on error
|
||||
reader = null;
|
||||
writer = null;
|
||||
readableStreamClosed = null;
|
||||
|
||||
Reference in New Issue
Block a user