- 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.
278 lines
9.2 KiB
Vue
278 lines
9.2 KiB
Vue
<script setup>
|
|
definePageMeta({
|
|
title: "Role Templates",
|
|
middleware: ["auth"],
|
|
requiresAuth: true,
|
|
breadcrumb: [
|
|
{ name: "Dashboard", path: "/dashboard" },
|
|
{ name: "Roles", path: "/roles" },
|
|
{ name: "Templates", path: "/roles/templates", type: "current" }
|
|
]
|
|
});
|
|
|
|
import { ref, reactive } from 'vue'
|
|
|
|
// Template state management
|
|
const templates = ref([
|
|
{
|
|
id: '1',
|
|
name: 'Administrator',
|
|
description: 'Full system access with all permissions',
|
|
permissions: {
|
|
menus: ['*'],
|
|
components: ['*'],
|
|
features: ['*']
|
|
}
|
|
},
|
|
{
|
|
id: '2',
|
|
name: 'Manager',
|
|
description: 'Department management with limited admin access',
|
|
permissions: {
|
|
menus: ['dashboard', 'users', 'reports'],
|
|
components: ['user.view', 'user.edit', 'reports.view'],
|
|
features: ['export.data', 'approve.requests']
|
|
}
|
|
},
|
|
{
|
|
id: '3',
|
|
name: 'Editor',
|
|
description: 'Content management with no admin access',
|
|
permissions: {
|
|
menus: ['dashboard', 'content'],
|
|
components: ['content.view', 'content.edit'],
|
|
features: ['export.data']
|
|
}
|
|
}
|
|
])
|
|
|
|
// Form state
|
|
const templateForm = reactive({
|
|
name: '',
|
|
description: '',
|
|
permissions: {
|
|
menus: [],
|
|
components: [],
|
|
features: []
|
|
}
|
|
})
|
|
|
|
// Available resources (in real app, this would be fetched from API)
|
|
const availableResources = reactive({
|
|
menus: [
|
|
{ id: 'dashboard', name: 'Dashboard', key: 'menu.dashboard' },
|
|
{ id: 'users', name: 'Users', key: 'menu.users' },
|
|
{ id: 'reports', name: 'Reports', key: 'menu.reports' },
|
|
{ id: 'content', name: 'Content', key: 'menu.content' }
|
|
],
|
|
components: [
|
|
{ id: 'user.view', name: 'View User', key: 'component.user.view' },
|
|
{ id: 'user.edit', name: 'Edit User', key: 'component.user.edit' },
|
|
{ id: 'content.view', name: 'View Content', key: 'component.content.view' },
|
|
{ id: 'content.edit', name: 'Edit Content', key: 'component.content.edit' },
|
|
{ id: 'reports.view', name: 'View Reports', key: 'component.reports.view' }
|
|
],
|
|
features: [
|
|
{ id: 'export.data', name: 'Export Data', key: 'feature.export.data' },
|
|
{ id: 'approve.requests', name: 'Approve Requests', key: 'feature.approve.requests' }
|
|
]
|
|
})
|
|
|
|
// Form handlers
|
|
const handleTemplateSubmit = (data) => {
|
|
const newTemplate = {
|
|
id: Date.now().toString(),
|
|
...data,
|
|
permissions: {
|
|
menus: data.permissions.menus || [],
|
|
components: data.permissions.components || [],
|
|
features: data.permissions.features || []
|
|
}
|
|
}
|
|
templates.value.push(newTemplate)
|
|
|
|
// Reset form
|
|
templateForm.name = ''
|
|
templateForm.description = ''
|
|
templateForm.permissions = {
|
|
menus: [],
|
|
components: [],
|
|
features: []
|
|
}
|
|
}
|
|
|
|
// Delete handler
|
|
const deleteTemplate = (id) => {
|
|
templates.value = templates.value.filter(template => template.id !== id)
|
|
}
|
|
|
|
// Clone handler
|
|
const cloneTemplate = (template) => {
|
|
const clonedTemplate = {
|
|
...template,
|
|
id: Date.now().toString(),
|
|
name: `${template.name} (Copy)`,
|
|
}
|
|
templates.value.push(clonedTemplate)
|
|
}
|
|
|
|
// Helper to format permission list
|
|
const formatPermissionList = (permissions) => {
|
|
if (permissions.includes('*')) return 'All permissions'
|
|
return permissions.length + ' permissions'
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<LayoutsBreadcrumb />
|
|
|
|
<!-- Header -->
|
|
<rs-card class="mb-6">
|
|
<template #body>
|
|
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between">
|
|
<div class="mb-4 lg:mb-0">
|
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">Role Templates</h1>
|
|
<p class="text-gray-600 dark:text-gray-400">Manage predefined role templates with preset permissions</p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Content -->
|
|
<div class="space-y-6">
|
|
<!-- Add Template Form -->
|
|
<rs-card>
|
|
<template #header>
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Create New Template</h3>
|
|
</template>
|
|
<template #body>
|
|
<FormKit
|
|
type="form"
|
|
:config="{ validationVisibility: 'submit' }"
|
|
@submit="handleTemplateSubmit"
|
|
:value="templateForm"
|
|
:actions="false"
|
|
>
|
|
<div class="space-y-6">
|
|
<!-- Basic Info -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<FormKit
|
|
type="text"
|
|
name="name"
|
|
label="Template Name"
|
|
validation="required"
|
|
placeholder="e.g., Department Manager"
|
|
/>
|
|
|
|
<FormKit
|
|
type="textarea"
|
|
name="description"
|
|
label="Description"
|
|
validation="required"
|
|
placeholder="Describe the role and its permissions"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Permissions -->
|
|
<div class="space-y-6">
|
|
<h4 class="text-lg font-medium text-gray-900 dark:text-white">Permissions</h4>
|
|
|
|
<!-- Menu Permissions -->
|
|
<div>
|
|
<FormKit
|
|
type="checkbox"
|
|
name="permissions.menus"
|
|
label="Menu Access"
|
|
:options="availableResources.menus.reduce((acc, menu) => {
|
|
acc[menu.id] = menu.name
|
|
return acc
|
|
}, {'*': 'All Menus'})"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Component Permissions -->
|
|
<div>
|
|
<FormKit
|
|
type="checkbox"
|
|
name="permissions.components"
|
|
label="Component Access"
|
|
:options="availableResources.components.reduce((acc, component) => {
|
|
acc[component.id] = component.name
|
|
return acc
|
|
}, {'*': 'All Components'})"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Feature Permissions -->
|
|
<div>
|
|
<FormKit
|
|
type="checkbox"
|
|
name="permissions.features"
|
|
label="Feature Access"
|
|
:options="availableResources.features.reduce((acc, feature) => {
|
|
acc[feature.id] = feature.name
|
|
return acc
|
|
}, {'*': 'All Features'})"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex justify-end">
|
|
<rs-button type="submit" variant="primary">
|
|
<Icon name="ph:plus" class="w-4 h-4 mr-2" />
|
|
Create Template
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
</FormKit>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Template List -->
|
|
<rs-card>
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white">Available Templates</h3>
|
|
<rs-badge variant="secondary">{{ templates.length }} templates</rs-badge>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
<div v-for="template in templates" :key="template.id" class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-6">
|
|
<div class="flex justify-between items-start mb-4">
|
|
<div>
|
|
<h4 class="text-lg font-medium text-gray-900 dark:text-white">{{ template.name }}</h4>
|
|
<p class="text-sm text-gray-600 dark:text-gray-400">{{ template.description }}</p>
|
|
</div>
|
|
<div class="flex space-x-2">
|
|
<rs-button @click="cloneTemplate(template)" variant="secondary-outline" size="sm">
|
|
<Icon name="ph:copy" class="w-4 h-4" />
|
|
</rs-button>
|
|
<rs-button @click="deleteTemplate(template.id)" variant="danger-outline" size="sm">
|
|
<Icon name="ph:trash" class="w-4 h-4" />
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-3">
|
|
<div class="flex items-center text-sm">
|
|
<Icon name="ph:list" class="w-4 h-4 mr-2 text-blue-600" />
|
|
<span class="text-gray-700 dark:text-gray-300">{{ formatPermissionList(template.permissions.menus) }}</span>
|
|
</div>
|
|
<div class="flex items-center text-sm">
|
|
<Icon name="ph:squares-four" class="w-4 h-4 mr-2 text-green-600" />
|
|
<span class="text-gray-700 dark:text-gray-300">{{ formatPermissionList(template.permissions.components) }}</span>
|
|
</div>
|
|
<div class="flex items-center text-sm">
|
|
<Icon name="ph:gear" class="w-4 h-4 mr-2 text-purple-600" />
|
|
<span class="text-gray-700 dark:text-gray-300">{{ formatPermissionList(template.permissions.features) }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
</div>
|
|
</div>
|
|
</template> |