- Updated README.md to reflect the new project name and provide an overview of the Role-Based Access Control (RBAC) system. - Added new components for RBAC management, including: - PermissionExample.vue: Demonstrates permission-based navigation. - GroupCard.vue: Displays group information and assigned roles. - PermissionMatrix.vue: Visual representation of permissions across roles and resources. - RoleTemplates.vue: Quick role templates for applying pre-configured permissions. - StatsCards.vue: Displays statistics related to users, groups, and roles. - Introduced useRbacPermissions.js for managing permission checks. - Created docker-compose.yml for PostgreSQL and Redis services. - Developed comprehensive documentation for application management and Authentik integration. - Added multiple pages for managing applications, groups, roles, and users, including bulk operations and templates. - Updated navigation structure to include new RBAC management paths.
221 lines
6.7 KiB
JavaScript
221 lines
6.7 KiB
JavaScript
export const useRbacPermissions = () => {
|
|
const permissionCache = ref(new Map())
|
|
const batchCache = ref(new Map())
|
|
|
|
/**
|
|
* Check if the current user has a specific permission
|
|
* @param {string} permissionKey - The unique permission key (e.g., 'menu.dashboard', 'component.user.edit_button')
|
|
* @param {string} action - The action to check (default: 'view')
|
|
* @returns {Promise<boolean>} - Whether the user has the permission
|
|
*/
|
|
const hasPermission = async (permissionKey, action = 'view') => {
|
|
// Check local cache first
|
|
const cacheKey = `${permissionKey}:${action}`
|
|
if (permissionCache.value.has(cacheKey)) {
|
|
return permissionCache.value.get(cacheKey)
|
|
}
|
|
|
|
try {
|
|
const { data } = await $fetch('/api/rbac/check-permission', {
|
|
method: 'POST',
|
|
body: { permissionKey, action }
|
|
})
|
|
|
|
// Cache result locally
|
|
permissionCache.value.set(cacheKey, data.hasPermission)
|
|
|
|
return data.hasPermission
|
|
} catch (error) {
|
|
console.error('Permission check failed:', error)
|
|
return false // Fail secure
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check multiple permissions at once (more efficient for bulk checks)
|
|
* @param {string[]} permissionKeys - Array of permission keys to check
|
|
* @param {string} action - The action to check for all keys (default: 'view')
|
|
* @returns {Promise<Object>} - Object with permission keys as keys and boolean results as values
|
|
*/
|
|
const hasPermissions = async (permissionKeys, action = 'view') => {
|
|
// Check cache for already known permissions
|
|
const results = {}
|
|
const uncachedKeys = []
|
|
|
|
for (const key of permissionKeys) {
|
|
const cacheKey = `${key}:${action}`
|
|
if (permissionCache.value.has(cacheKey)) {
|
|
results[key] = permissionCache.value.get(cacheKey)
|
|
} else {
|
|
uncachedKeys.push(key)
|
|
}
|
|
}
|
|
|
|
// If all permissions are cached, return immediately
|
|
if (uncachedKeys.length === 0) {
|
|
return results
|
|
}
|
|
|
|
try {
|
|
const { data } = await $fetch('/api/rbac/check-permissions-batch', {
|
|
method: 'POST',
|
|
body: { permissionKeys: uncachedKeys, action }
|
|
})
|
|
|
|
// Cache results locally and add to final results
|
|
Object.entries(data.permissions).forEach(([key, hasAccess]) => {
|
|
const cacheKey = `${key}:${action}`
|
|
permissionCache.value.set(cacheKey, hasAccess)
|
|
results[key] = hasAccess
|
|
})
|
|
|
|
return results
|
|
} catch (error) {
|
|
console.error('Batch permission check failed:', error)
|
|
// Return false for uncached keys
|
|
uncachedKeys.forEach(key => {
|
|
results[key] = false
|
|
})
|
|
return results
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if user can access a specific menu item
|
|
* @param {string} menuPath - Menu path or key
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
const canAccessMenu = async (menuPath) => {
|
|
const menuKey = menuPath.startsWith('menu.') ? menuPath : `menu.${menuPath.replace(/^\//, '').replace(/\//g, '.')}`
|
|
return await hasPermission(menuKey, 'view')
|
|
}
|
|
|
|
/**
|
|
* Check if user can see a specific component
|
|
* @param {string} componentKey - Component permission key
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
const canSeeComponent = async (componentKey) => {
|
|
const fullKey = componentKey.startsWith('component.') ? componentKey : `component.${componentKey}`
|
|
return await hasPermission(fullKey, 'view')
|
|
}
|
|
|
|
/**
|
|
* Check if user can perform a specific feature action
|
|
* @param {string} featureKey - Feature permission key
|
|
* @param {string} action - Action to check (create, edit, delete, approve, etc.)
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
const canPerformAction = async (featureKey, action) => {
|
|
const fullKey = featureKey.startsWith('feature.') ? featureKey : `feature.${featureKey}`
|
|
return await hasPermission(fullKey, action)
|
|
}
|
|
|
|
/**
|
|
* Clear the permission cache (useful after role changes)
|
|
*/
|
|
const clearCache = () => {
|
|
permissionCache.value.clear()
|
|
batchCache.value.clear()
|
|
}
|
|
|
|
/**
|
|
* Pre-load permissions for better performance
|
|
* @param {string[]} permissionKeys - Array of permission keys to preload
|
|
*/
|
|
const preloadPermissions = async (permissionKeys) => {
|
|
await hasPermissions(permissionKeys)
|
|
}
|
|
|
|
/**
|
|
* Get cached permission state (useful for reactive UI updates)
|
|
* @param {string} permissionKey
|
|
* @param {string} action
|
|
* @returns {boolean|null} - null if not cached, boolean if cached
|
|
*/
|
|
const getCachedPermission = (permissionKey, action = 'view') => {
|
|
const cacheKey = `${permissionKey}:${action}`
|
|
return permissionCache.value.has(cacheKey) ? permissionCache.value.get(cacheKey) : null
|
|
}
|
|
|
|
return {
|
|
hasPermission,
|
|
hasPermissions,
|
|
canAccessMenu,
|
|
canSeeComponent,
|
|
canPerformAction,
|
|
clearCache,
|
|
preloadPermissions,
|
|
getCachedPermission
|
|
}
|
|
}
|
|
|
|
// Permission key constants for type safety and consistency
|
|
export const PERMISSION_KEYS = {
|
|
// Menu permissions
|
|
MENU: {
|
|
DASHBOARD: 'menu.dashboard',
|
|
USERS: 'menu.users',
|
|
USERS_LIST: 'menu.users.list',
|
|
USERS_CREATE: 'menu.users.create',
|
|
RBAC: 'menu.rbac',
|
|
RBAC_ROLES: 'menu.rbac.roles',
|
|
RBAC_PERMISSIONS: 'menu.rbac.permissions',
|
|
REPORTS: 'menu.reports',
|
|
SETTINGS: 'menu.settings'
|
|
},
|
|
|
|
// Component permissions
|
|
COMPONENT: {
|
|
USER_EDIT_BUTTON: 'component.user.edit_button',
|
|
USER_DELETE_BUTTON: 'component.user.delete_button',
|
|
USER_BULK_ACTIONS: 'component.user.bulk_actions',
|
|
PROFILE_SENSITIVE_INFO: 'component.profile.sensitive_info',
|
|
FINANCIAL_DATA: 'component.financial.data',
|
|
APPROVAL_WORKFLOW: 'component.approval.workflow'
|
|
},
|
|
|
|
// Feature permissions
|
|
FEATURE: {
|
|
EXPORT_DATA: 'feature.export.data',
|
|
APPROVE_REQUESTS: 'feature.approve.requests',
|
|
SYSTEM_BACKUP: 'feature.system.backup',
|
|
USER_IMPERSONATION: 'feature.user.impersonation',
|
|
BULK_USER_OPERATIONS: 'feature.bulk.user_operations'
|
|
}
|
|
}
|
|
|
|
// Common action types
|
|
export const PERMISSION_ACTIONS = {
|
|
VIEW: 'view',
|
|
CREATE: 'create',
|
|
EDIT: 'edit',
|
|
DELETE: 'delete',
|
|
APPROVE: 'approve',
|
|
EXPORT: 'export',
|
|
IMPORT: 'import'
|
|
}
|
|
|
|
// Helper function for reactive permission checking in templates
|
|
export const useReactivePermission = (permissionKey, action = 'view') => {
|
|
const { hasPermission } = useRbacPermissions()
|
|
const isAllowed = ref(false)
|
|
const isLoading = ref(true)
|
|
|
|
const checkPermission = async () => {
|
|
try {
|
|
isLoading.value = true
|
|
isAllowed.value = await hasPermission(permissionKey, action)
|
|
} catch (error) {
|
|
console.error('Permission check failed:', error)
|
|
isAllowed.value = false
|
|
} finally {
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
// Check permission on component mount
|
|
onMounted(checkPermission)
|
|
|
|
return { isAllowed, isLoading, checkPermission }
|
|
}
|