- 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.
162 lines
5.1 KiB
Vue
162 lines
5.1 KiB
Vue
<template>
|
|
<rs-card>
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white">{{ group.name }}</h3>
|
|
<p class="text-sm text-gray-600 dark:text-gray-400">{{ group.description }}</p>
|
|
</div>
|
|
<div class="flex items-center space-x-2">
|
|
<rs-badge variant="info">{{ group.userCount }} users</rs-badge>
|
|
<rs-badge
|
|
:variant="group.authentikSynced ? 'success' : 'warning'"
|
|
class="text-xs"
|
|
>
|
|
{{ group.authentikSynced ? 'Synced' : 'Manual' }}
|
|
</rs-badge>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-4">
|
|
<!-- Role Assignment Section -->
|
|
<div>
|
|
<div class="flex items-center justify-between mb-3">
|
|
<h4 class="text-sm font-medium text-gray-900 dark:text-white">Assigned Roles</h4>
|
|
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
{{ assignedRoleCount }} of {{ availableRoles.length }} roles
|
|
</span>
|
|
</div>
|
|
|
|
<div class="space-y-2">
|
|
<label v-for="role in availableRoles" :key="role.id" class="flex items-center group">
|
|
<input
|
|
type="checkbox"
|
|
:checked="isRoleAssigned(role.id)"
|
|
@change="handleRoleToggle(role.id, $event)"
|
|
class="h-4 w-4 text-primary focus:ring-primary border-gray-300 rounded transition-colors"
|
|
/>
|
|
<div class="ml-3 flex-1 flex items-center justify-between">
|
|
<div>
|
|
<span class="text-sm text-gray-900 dark:text-white">{{ role.name }}</span>
|
|
<p class="text-xs text-gray-500 dark:text-gray-400">{{ role.description }}</p>
|
|
</div>
|
|
<div class="flex items-center space-x-2">
|
|
<rs-badge variant="secondary" class="text-xs">{{ role.userCount }}</rs-badge>
|
|
<button
|
|
v-if="isRoleAssigned(role.id)"
|
|
@click="$emit('view-role-details', role.id)"
|
|
class="opacity-0 group-hover:opacity-100 text-primary hover:text-primary/80 text-xs transition-opacity"
|
|
>
|
|
View Details
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="pt-4 border-t border-gray-200 dark:border-gray-700">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex space-x-2">
|
|
<rs-button
|
|
@click="$emit('apply-template', 'admin')"
|
|
variant="primary-outline"
|
|
size="sm"
|
|
class="text-xs"
|
|
>
|
|
Admin Template
|
|
</rs-button>
|
|
<rs-button
|
|
@click="$emit('apply-template', 'viewer')"
|
|
variant="primary-outline"
|
|
size="sm"
|
|
class="text-xs"
|
|
>
|
|
Viewer Template
|
|
</rs-button>
|
|
</div>
|
|
|
|
<rs-button
|
|
@click="$emit('manage-users')"
|
|
variant="primary-outline"
|
|
size="sm"
|
|
class="text-xs"
|
|
>
|
|
<Icon name="ph:users" class="w-3 h-3 mr-1" />
|
|
Manage Users
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Group Metadata -->
|
|
<div class="pt-3 border-t border-gray-200 dark:border-gray-700">
|
|
<div class="grid grid-cols-2 gap-4 text-xs text-gray-500 dark:text-gray-400">
|
|
<div>
|
|
<span class="font-medium">Authentik UUID:</span>
|
|
<p class="font-mono mt-1 break-all">{{ group.authentikUUID }}</p>
|
|
</div>
|
|
<div>
|
|
<span class="font-medium">Last Sync:</span>
|
|
<p class="mt-1">{{ formatDate(group.lastSync) }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from 'vue'
|
|
|
|
const props = defineProps({
|
|
group: {
|
|
type: Object,
|
|
required: true
|
|
},
|
|
availableRoles: {
|
|
type: Array,
|
|
required: true
|
|
},
|
|
assignedRoles: {
|
|
type: Array,
|
|
default: () => []
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits([
|
|
'role-changed',
|
|
'apply-template',
|
|
'manage-users',
|
|
'view-role-details'
|
|
])
|
|
|
|
const assignedRoleCount = computed(() => props.assignedRoles.length)
|
|
|
|
const isRoleAssigned = (roleId) => {
|
|
return props.assignedRoles.includes(roleId)
|
|
}
|
|
|
|
const handleRoleToggle = (roleId, event) => {
|
|
const assigned = event.target.checked
|
|
emit('role-changed', { groupId: props.group.id, roleId, assigned })
|
|
}
|
|
|
|
const formatDate = (dateString) => {
|
|
if (!dateString) return 'Never'
|
|
return new Date(dateString).toLocaleDateString('en-US', {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
})
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.group:hover .opacity-0 {
|
|
@apply opacity-100;
|
|
}
|
|
</style> |