Permission management with Role-Based Access Control (RBAC) and Attribute-Based Access Control (ABAC) support.
Features
- Role-Based Access Control (RBAC)
- Attribute-Based Access Control (ABAC) with context
- Functional permission conditions
- Token-based permission storage
- Adapter pattern for custom permission systems
Installation
import { createPermissionsPlugin } from '@vuetify/v0'
const app = createApp(App)
app.use(createPermissionsPlugin({
permissions: {
admin: [
['read', 'users'],
['write', 'users'],
['delete', 'users'],
],
editor: [
['read', 'posts'],
['write', 'posts'],
],
},
}))
Basic Usage
<script setup lang="ts">
import { usePermissions } from '@vuetify/v0'
import { ref } from 'vue'
const permissions = usePermissions()
const userRole = ref('editor')
const canEditPosts = computed(() =>
permissions.can(userRole.value, 'write', 'posts')
)
</script>
<template>
<div>
<button v-if="canEditPosts">Edit Post</button>
</div>
</template>
API Reference
createPermissionsPlugin()
Creates a permissions plugin.
Plugin configurationRecord of roles and their permissionsEach permission is a tuple:
[actions, subjects, condition?]
actions: String or array of action names (e.g., ‘read’, ‘write’)
subjects: String or array of subject names (e.g., ‘users’, ‘posts’)
condition: Optional boolean or function for conditional permissions
Example:{
admin: [
[['read', 'write', 'delete'], ['users', 'posts']],
],
editor: [
['write', 'posts', (ctx) => ctx.isOwner],
],
}
Custom permission adapter (defaults to Vuetify0PermissionAdapter)
namespace
string
default:"'v0:permissions'"
The namespace for the permissions context
PermissionContext
Number of registered permission entries
can
(id: ID, action: string, subject: string, context?: Record<string, any>) => boolean
Check if a role has permission to perform an action on a subjectParameters:
id: Role identifier (e.g., ‘admin’, ‘editor’)
action: Action name (e.g., ‘read’, ‘write’, ‘delete’)
subject: Subject name (e.g., ‘users’, ‘posts’)
context: Optional context object for conditional permissions
Returns: true if permission is granted, false otherwiseExamples:permissions.can('admin', 'read', 'users') // true
permissions.can('editor', 'delete', 'users') // false
permissions.can('owner', 'edit', 'posts', { isOwner: true }) // true
get
(path: string) => PermissionTicket | undefined
Get a permission entry by pathPath format: role.action.subjectExample:const ticket = permissions.get('admin.read.users')
console.log(ticket.value) // true or function
has
(path: string) => boolean
Check if a permission entry exists
Simple RBAC
Basic role-based permissions:
app.use(createPermissionsPlugin({
permissions: {
admin: [
['read', 'users'],
['write', 'users'],
['delete', 'users'],
],
viewer: [
['read', 'users'],
],
},
}))
// Usage
const permissions = usePermissions()
permissions.can('admin', 'delete', 'users') // true
permissions.can('viewer', 'delete', 'users') // false
Multiple Actions
permissions: {
editor: [
[['read', 'write'], 'posts'],
],
}
// Both actions are granted
permissions.can('editor', 'read', 'posts') // true
permissions.can('editor', 'write', 'posts') // true
Multiple Subjects
permissions: {
viewer: [
['read', ['posts', 'comments', 'users']],
],
}
// Can read all subjects
permissions.can('viewer', 'read', 'posts') // true
permissions.can('viewer', 'read', 'comments') // true
permissions.can('viewer', 'read', 'users') // true
Matrix Permissions
permissions: {
admin: [
[['read', 'write', 'delete'], ['users', 'posts', 'comments']],
],
}
// All combinations are granted
permissions.can('admin', 'read', 'users') // true
permissions.can('admin', 'write', 'posts') // true
permissions.can('admin', 'delete', 'comments') // true
Conditional Permissions (ABAC)
Boolean Conditions
permissions: {
moderator: [
['delete', 'comments', true], // Always allowed
['delete', 'posts', false], // Never allowed
],
}
Function Conditions
permissions: {
owner: [
['edit', 'posts', (ctx) => ctx.isOwner === true],
['delete', 'posts', (ctx) => ctx.isOwner === true],
],
}
// Usage
const permissions = usePermissions()
const post = { authorId: 123 }
const currentUserId = 123
permissions.can('owner', 'edit', 'posts', {
isOwner: post.authorId === currentUserId
}) // true
permissions.can('owner', 'edit', 'posts', {
isOwner: false
}) // false
Complex Context
permissions: {
editor: [
['edit', 'posts', (ctx) => {
return ctx.userId === ctx.postAuthorId ||
ctx.userRoles.includes('senior-editor')
}],
],
}
// Usage
permissions.can('editor', 'edit', 'posts', {
userId: 123,
postAuthorId: 456,
userRoles: ['editor', 'senior-editor'],
}) // true (senior editor)
Examples
Role-Based UI
<script setup lang="ts">
import { usePermissions } from '@vuetify/v0'
import { ref, computed } from 'vue'
const permissions = usePermissions()
const userRole = ref('editor')
const canCreatePost = computed(() =>
permissions.can(userRole.value, 'write', 'posts')
)
const canDeletePost = computed(() =>
permissions.can(userRole.value, 'delete', 'posts')
)
</script>
<template>
<div>
<button v-if="canCreatePost">Create Post</button>
<button v-if="canDeletePost">Delete Post</button>
</div>
</template>
Ownership Check
<script setup lang="ts">
import { usePermissions } from '@vuetify/v0'
import { ref } from 'vue'
const permissions = usePermissions()
const currentUser = ref({ id: 123, role: 'editor' })
const post = ref({ id: 1, authorId: 123 })
const canEdit = computed(() => {
return permissions.can(
currentUser.value.role,
'edit',
'posts',
{ isOwner: post.value.authorId === currentUser.value.id }
)
})
</script>
<template>
<article>
<h1>{{ post.title }}</h1>
<button v-if="canEdit">Edit</button>
</article>
</template>
Multi-Role User
<script setup lang="ts">
import { usePermissions } from '@vuetify/v0'
import { ref } from 'vue'
const permissions = usePermissions()
const userRoles = ref(['editor', 'moderator'])
const canPerformAction = (action: string, subject: string) => {
return userRoles.value.some(role =>
permissions.can(role, action, subject)
)
}
</script>
<template>
<div>
<button v-if="canPerformAction('write', 'posts')">Edit Post</button>
<button v-if="canPerformAction('delete', 'comments')">Delete Comment</button>
</div>
</template>
Advanced Usage
Custom Adapter
import type { PermissionAdapter } from '@vuetify/v0'
class CaslAdapter implements PermissionAdapter {
constructor(private ability: any) {}
can(id: ID, action: string, subject: string, context: any, tokens: any) {
return this.ability.can(action, subject)
}
}
app.use(createPermissionsPlugin({
adapter: new CaslAdapter(ability),
}))
Dynamic Permissions
const permissions = usePermissions()
// Get permission ticket
const ticket = permissions.get('admin.read.users')
if (ticket) {
console.log('Value:', ticket.value)
// For function conditions
if (typeof ticket.value === 'function') {
const allowed = ticket.value({ userId: 123 })
console.log('Allowed:', allowed)
}
}
Hierarchical Roles
const roleHierarchy = {
admin: ['editor', 'moderator', 'viewer'],
editor: ['viewer'],
moderator: ['viewer'],
viewer: [],
}
const hasPermission = (userRole: string, action: string, subject: string) => {
const roles = [userRole, ...roleHierarchy[userRole]]
return roles.some(role => permissions.can(role, action, subject))
}