Installations
npm install @casl/vue
Developer Guide
Typescript
No
Module System
CommonJS, ESM
Node Version
18.19.0
NPM Version
10.2.3
Score
79
Supply Chain
94
Quality
76.9
Maintenance
100
Vulnerability
100
License
Releases
@casl/angular@9.0.3
Published on 08 Jan 2025
@casl/ability@6.7.3
Published on 05 Jan 2025
@casl/mongoose@8.0.3
Published on 05 Jan 2025
@casl/aurelia@1.3.1
Published on 05 Jan 2025
@casl/angular@9.0.2
Published on 05 Jan 2025
@casl/prisma@1.5.1
Published on 05 Jan 2025
Contributors
Unable to fetch Contributors
Languages
JavaScript (51.39%)
TypeScript (47.08%)
HTML (0.69%)
Shell (0.53%)
CSS (0.31%)
Developer
Download Statistics
Total Downloads
6,999,371
Last Day
7,426
Last Week
33,764
Last Month
156,608
Last Year
2,087,016
GitHub Statistics
6,208 Stars
1,657 Commits
276 Forks
39 Watching
32 Branches
67 Contributors
Bundle Size
1.86 kB
Minified
869.00 B
Minified + Gzipped
Package Meta Information
Latest Version
2.2.2
Package Id
@casl/vue@2.2.2
Unpacked Size
42.37 kB
Size
10.53 kB
File Count
14
NPM Version
10.2.3
Node Version
18.19.0
Publised On
14 Feb 2024
Total Downloads
Cumulative downloads
Total Downloads
6,999,371
Last day
-8.6%
7,426
Compared to previous day
Last week
-18.9%
33,764
Compared to previous week
Last month
6.6%
156,608
Compared to previous month
Last year
15.1%
2,087,016
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
Peer Dependencies
2
Dev Dependencies
6
CASL Vue
This package allows to integrate @casl/ability
with Vue 3 application. So, you can show or hide UI elements based on user ability to see them.
Installation
For Vue 2.x:
1npm install @casl/vue@1.x @casl/ability 2# or 3yarn add @casl/vue@1.x @casl/ability 4# or 5pnpm add @casl/vue@1.x @casl/ability
For Vue 3.x:
1npm install @casl/vue @casl/ability 2# or 3yarn add @casl/vue @casl/ability 4# or 5pnpm add @casl/vue @casl/ability
Getting started
This package provides a Vue plugin, several hooks for new Vue Composition API and Can
component.
The plugin
The plugin provides reactive Ability
instance and optionally defines $ability
and $can
global properties, in the same way as it was for Vue 2.x. The only difference with the previous version is that it requires Ability
instance to be passed as a mandatory argument:
1import { createApp } from 'vue'; 2import { abilitiesPlugin } from '@casl/vue'; 3import ability from './services/ability'; 4 5createApp() 6 .use(abilitiesPlugin, ability, { 7 useGlobalProperties: true 8 }) 9 .mount('#app');
Later, we can use either $ability
or $can
method in any component:
1<template> 2 <div v-if="$can('create', 'Post')"> 3 <a @click="createPost">Add Post</a> 4 </div> 5</template>
globalProperties
is the same concept as global variables which may make life a bit more complicated because any component has access to them (i.e., implicit dependency) and we need to ensure they don't introduce name collisions by prefixing them. So, instead of exposing $ability
and $can
as globals, we can use provide/inject API to inject $ability
:
1createApp() 2 .use(abilitiesPlugin, ability) 3 .mount('#app');
And to inject an Ability
instance in a component, we can use ABILITY_TOKEN
:
1<template> 2 <div> 3 <div v-if="$ability.can('create', 'Post')"> 4 <a @click="createPost">Add Post</a> 5 </div> 6 </div> 7</template> 8 9<script> 10import { ABILITY_TOKEN } from '@casl/vue'; 11 12export default { 13 inject: { 14 $ability: { from: ABILITY_TOKEN } 15 } 16} 17</script>
This is a bit more verbose but allows us to be explicit. This works especially good with new Composition API:
1<template> 2 <div> 3 <div v-if="can('create', 'Post')"> 4 <a @click="createPost">Add Post</a> 5 </div> 6 </div> 7</template> 8 9<script> 10import { useAbility } from '@casl/vue'; 11 12export default { 13 setup() { 14 // some code 15 const { can } = useAbility(); 16 17 return { 18 // other props 19 can 20 }; 21 } 22} 23</script>
provideAbility hook
Very rarely, we may need to provide a different Ability
instance for a sub-tree of components, and to do this we can use provideAbility
hook:
1<template> 2 <!-- a template --> 3</template> 4 5<script> 6import { provideAbility } from '@casl/vue'; 7import { defineAbility } from '@casl/ability'; 8 9export default { 10 setup() { 11 const myCustomAbility = defineAbility((can) => { 12 // ... 13 }); 14 15 provideAbility(myCustomAbility) 16 } 17} 18</script>
See CASL guide to learn how to define
Ability
instance.
Can component
There is an alternative way we can check permissions in the app, by using Can
component. Can
component is not registered by the plugin, so we can decide whether we want to use component or v-if
+ $can
method. Also, this helps tree shaking to remove it if we decide to not use it.
To register component globally, we can use global API (we can also register component locally in components that use it):
1import { Can, abilitiesPlugin } from '@casl/vue'; 2 3createApp() 4 .use(abilitiesPlugin, ability) 5 .component(Can.name, Can) // component registration 6 .mount('#app');
And this is how we can use it:
1<template> 2 <Can I="create" a="Post"> 3 <a @click="createPost">Add Post</a> 4 </Can> 5</template>
It accepts default slot and 5 properties:
-
do
- name of the action (e.g.,read
,update
). Has an aliasI
-
on
- checked subject. Hasa
,an
,this
aliases -
field
- checked field1<template> 2 <Can I="read" :this="post" field="title"> 3 Yes, you can do this! ;) 4 </Can> 5</template>
-
not
- inverts ability check and show UI if user cannot do some action:1<template> 2 <Can not I="create" a="Post"> 3 You are not allowed to create a post 4 </Can> 5</template>
-
passThrough
- renders children in spite of whatability.can
returns. This is useful for creating custom components based onCan
. For example, if you need to disable button based on user permissions:1<template> 2 <div> 3 <Can I="delete" a="Post" passThrough v-slot="{ allowed }"> 4 <button :disabled="!allowed">Delete post</button> 5 </Can> 6 </div> 7</template>
Property names and aliases
As you can see from the code above, the component name and its property names and values create an English sentence, actually a question. The example above reads as "Can I delete a Post?".
There are several other property aliases which allow constructing a readable question. And here is a guidance to help you do this:
-
use the
a
(oran
) alias when you check by Type1<Can I="read" a="Post">...</Can>
-
use
this
alias when you check action on a particular instance. So, the question can be read as "Can I read this particular post?"1<Can I="read" :this="post">...</Can>
-
use
do
andon
if you are bored and don't want to make your code more readable :)1<Can do="read" :on="post">...</Can> 2<Can do="read" :on="post" field="title">...</Can>
Component vs reactive Ability
Let's consider PROS and CONS of both solutions in order to make the decision.
Can Component:
PROS:
- declarative
- can cache permissions check results until props or ability changes (currently does not)
CONS:
- more expensive to create
- adds nesting in template
- harder to use in complex boolean expressions
- harder to pass permission check as a prop to another component
Reactive Ability:
PROS:
- easy to use
- declarative in template with
v-if
- easy to pass as a prop to another component
- easy to use in complex boolean expressions (either in js or in template)
CONS:
- more expensive to check, conditions are re-evaluated on each re-render
Despite the fact that reactive ability check is a bit more expensive, they are still very fast and it's recommended to use reactive ability instead of <Can>
component.
TypeScript support
The package is written in TypeScript, so don't worry that you need to keep all the properties and aliases in mind. If you use TypeScript, your IDE will suggest you the correct usage and TypeScript will warn you if you make a mistake.
There are few ways to use TypeScript in a Vue app, depending on your preferences. But let's first define our AppAbility
type:
1import { Ability, AbilityClass } from '@casl/ability'; 2 3type Actions = 'create' | 'read' | 'update' | 'delete'; 4type Subjects = 'Article' | 'User' 5 6export type AppAbility = Ability<[Actions, Subjects]>; 7export const AppAbility = Ability as AbilityClass<AppAbility>;
Augment Vue types
There is no other way for TypeScript to know types of global properties without augmentation. To do this, let's add src/shims-ability.d.ts
file with the next content:
1import { AppAbility } from './AppAbility' 2 3declare module 'vue' { 4 interface ComponentCustomProperties { 5 $ability: AppAbility; 6 $can(this: this, ...args: Parameters<this['$ability']['can']>): boolean; 7 } 8}
Composition API
With composition API, we don't need to augment Vue types and can use useAbility
hook:
1import { useAbility } from '@casl/vue'; 2import { AppAbility } from './AppAbility'; 3 4export default { 5 setup(props) { 6 const { can } = useAbility<AppAbility>(); 7 8 return () => can('read', 'Post') ? 'Yes' : 'No'; 9 } 10}
Additionally, we can create a separate useAppAbility
hook, so we don't need to import useAbility
and AppAbility
in every component we want to check permissions but instead just import a single hook:
1import { useAbility } from '@casl/vue'; 2import { AppAbility } from '../AppAbility'; 3 4export const useAppAbility = () => useAbility<AppAbility>();
Options API
It's also possible to use @casl/vue
and TypeScript with options API. By default, ABILITY_TOKEN
is typed as InjectionKey<Ability>
, to cast it to InjectionKey<AppAbility>
, we need to use a separate variable:
1import { InjectionKey } from 'vue'; 2import { ABILITY_TOKEN } from '@casl/vue'; 3 4// previous content that defines `AppAbility` 5 6export const TOKEN = ABILITY_TOKEN as InjectionKey<AppAbility>;
and now, when we inject AppAbility
instance, we will have the correct types:
1<script lang="ts"> 2import { defineComponent } from 'vue'; 3import { TOKEN } from './AppAbility'; 4 5export default defineComponent({ 6 inject: { 7 ability: { from: TOKEN } 8 }, 9 created() { 10 this.ability // AppAbility 11 } 12}); 13</script>
Read Vue TypeScript for more details.
Update Ability instance
Majority of applications that need permission checking support have something like AuthService
or LoginService
or Session
service (name it as you wish) which is responsible for user login/logout functionality. Whenever user login (and logout), we need to update Ability
instance with new rules. Usually you will do this in your LoginComponent
.
Let's imagine that server returns user with a role on login:
1<template> 2 <form @submit.prevent="login"> 3 <input type="email" v-model="email" /> 4 <input type="password" v-model="password" /> 5 <button type="submit">Login</button> 6 </form> 7</template> 8 9<script> 10import { AbilityBuilder, Ability } from '@casl/ability'; 11import { ABILITY_TOKEN } from '@casl/vue'; 12 13export default { 14 name: 'LoginForm', 15 inject: { 16 $ability: { from: ABILITY_TOKEN } 17 }, 18 data: () => ({ 19 email: '', 20 password: '' 21 }), 22 methods: { 23 login() { 24 const { email, password } = this; 25 const params = { method: 'POST', body: JSON.stringify({ email, password }) }; 26 27 return fetch('path/to/api/login', params) 28 .then(response => response.json()) 29 .then(({ user }) => this.updateAbility(user)); 30 }, 31 updateAbility(user) { 32 const { can, rules } = new AbilityBuilder(Ability); 33 34 if (user.role === 'admin') { 35 can('manage', 'all'); 36 } else { 37 can('read', 'all'); 38 } 39 40 this.$ability.update(rules); 41 } 42 } 43}; 44</script>
See Define rules to get more information of how to define
Ability
Want to help?
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on guidelines for contributing.
If you'd like to help us sustain our community and project, consider to become a financial contributor on Open Collective
See Support CASL for details
License
![Empty State](/_next/static/media/empty.e5fae2e5.png)
No vulnerabilities found.
Reason
30 commit(s) and 17 issue activity found in the last 90 days -- score normalized to 10
Reason
no binaries found in the repo
Reason
license file detected
Details
- Info: project has a license file: LICENSE:0
- Info: FSF or OSI recognized license: MIT License: LICENSE:0
Reason
Found 0/27 approved changesets -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
- Warn: no topLevel permission defined: .github/workflows/docs.yml:1
- Warn: no topLevel permission defined: .github/workflows/main.yml:1
- Warn: no topLevel permission defined: .github/workflows/release.yml:1
- Info: no jobLevel write permissions found
Reason
dangerous workflow patterns detected
Details
- Warn: script injection with untrusted input ' github.head_ref ': .github/workflows/release.yml:49
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
- Warn: no security policy file detected
- Warn: no security file to analyze
- Warn: no security file to analyze
- Warn: no security file to analyze
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/docs.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/stalniy/casl/docs.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/docs.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/stalniy/casl/docs.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/stalniy/casl/main.yml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/main.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/stalniy/casl/main.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:38: update your workflow using https://app.stepsecurity.io/secureworkflow/stalniy/casl/main.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/stalniy/casl/release.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/stalniy/casl/release.yml/master?enable=pin
- Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/stalniy/casl/release.yml/master?enable=pin
- Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/stalniy/casl/release.yml/master?enable=pin
- Warn: npmCommand not pinned by hash: .github/workflows/docs.yml:38
- Info: 0 out of 7 GitHub-owned GitHubAction dependencies pinned
- Info: 0 out of 2 third-party GitHubAction dependencies pinned
- Info: 0 out of 1 npmCommand dependencies pinned
Reason
project is not fuzzed
Details
- Warn: no fuzzer integrations found
Reason
branch protection not enabled on development/release branches
Details
- Warn: branch protection not enabled for branch 'master'
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
- Warn: 0 commits out of 15 are checked with a SAST tool
Reason
34 existing vulnerabilities detected
Details
- Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92
- Warn: Project is vulnerable to: GHSA-qwcr-r2fm-qrc7
- Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg
- Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x
- Warn: Project is vulnerable to: GHSA-phwq-j96m-2c2q
- Warn: Project is vulnerable to: GHSA-ghr5-ch3p-vcr6
- Warn: Project is vulnerable to: GHSA-rv95-896h-c2vc
- Warn: Project is vulnerable to: GHSA-qw6h-vgh9-j6wx
- Warn: Project is vulnerable to: GHSA-jchw-25xp-jwwc
- Warn: Project is vulnerable to: GHSA-cxjh-pqwp-8mfp
- Warn: Project is vulnerable to: GHSA-pfq8-rq6v-vf5m
- Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h
- Warn: Project is vulnerable to: GHSA-6vfc-qv3f-vr6c
- Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv
- Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3
- Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j
- Warn: Project is vulnerable to: GHSA-rhx6-c78j-4q9w
- Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp
- Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm
- Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw
- Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg
- Warn: Project is vulnerable to: GHSA-cm22-4g7w-348p
- Warn: Project is vulnerable to: GHSA-4wf5-vphf-c2xc
- Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q
- Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275
- Warn: Project is vulnerable to: GHSA-c7qv-q95q-8v27
- Warn: Project is vulnerable to: GHSA-5v2h-r2cx-5xgj
- Warn: Project is vulnerable to: GHSA-rrrm-qjm4-v8hf
- Warn: Project is vulnerable to: GHSA-m7xq-9374-9rvx
- Warn: Project is vulnerable to: GHSA-vg7j-7cwx-8wgw
- Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55
- Warn: Project is vulnerable to: GHSA-hj9c-8jmm-8c52
- Warn: Project is vulnerable to: GHSA-x2pg-mjhr-2m5x
- Warn: Project is vulnerable to: GHSA-vg6x-rcgg-rjx6
Score
2.2
/10
Last Scanned on 2025-01-27
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More