Roles and Permissions
As your app grows, you’ll want more control over how permissions are applied in real scenarios:
- Only edit their own blog posts (not someone else’s)
- View all values on a page, but only edit a select few
- Invite another user to edit blogs in a specific category
- Allow someone to edit a draft article, but only let the admin publish the page
You assign permissions to roles, and then hand out those roles to your users. But you can also assign permissions directly to users, without using roles. This is useful for one-off permissions that don't fit into a role, or for temporary access.
Suppose you want only certain users to be able to edit blog posts. You can check for a required permission like blogs. If the user has this permission, they can edit all blogs. If not, access is denied. This makes it easy to control who can do what, just by checking for the right permission string.
You can check permissions in your Blade views like this:
@if ($user->can('blogs'))
<article></article>
@endif| Required | Granted | Has Access? |
|---|---|---|
| blogs | blogs | ✔️ |
| blogs | homepage | ✖️ |
You can use paths to control access to specific fields or sub-resources. For example, blog/title refers to the title field of a blog. This allows you to grant fine-grained permissions for different fields and resources.
If a user is granted blog, they can access all fields of a blog. If a user is granted blog/title, they can only access or edit the title field.
Example:
- An admin with Granted Permission
blogcan create and edit the entire blog, including all fields. - An SEO specialist with Granted Permission
blog/titlecan only edit the title of the blog.
@if ($user->can('blog/title'))
<input name="title" />
@endif| Requested | Granted | Has Access? |
|---|---|---|
| blog/title | blog | ✔️ |
| blog/title | blog/title | ✔️ |
| blog/title | blog/content | ✖️ |
Permissions can be extended to specify what actions a user can perform on a resource. The path determines which part of the system the user can manage, and the action on a permission lets you control what actions are allowed.
-
blogs— allows both reading and writing -
blogs.read— allows only reading -
blogs.write— allows only writing
This applies to both requested and granted permissions. For example, you can request blogs.write and only get access if your granted permissions include blogs or blogs.write.
@if ($user->can('blogs.write'))
<input></input>
@endifThis way, both blogs.write and blogs Granted Permission allow editing the blog.
| Requested | Granted | Has Access? |
|---|---|---|
| blogs | blogs | ✔️ |
| blogs | blogs.read | ✖️ |
| blogs | blogs.write | ✖️ |
| blogs.write | blogs | ✔️ |
| blogs.write | blogs.read | ✖️ |
| blogs.write | blogs.write | ✔️ |
You can also define other activities besides 'write', such as 'blog.delete', to control custom actions on your resources.
For example:
-
blog.delete— allows deleting a blog
With a query, you can make your permissions even more fine-grained by specifying parameters for a resource. This allows you to control access to specific subsets of data.
For example:
-
blogs?author_id=123— only allows access to blogs by author 123 -
blogs?album=photos_2025— only allows access to blogs in the album 'photos_2025'
You can check for a specific query in Blade like this:
@if ($user->can('blogs', ['author_id' => 123]))
<button>Edit your own blogs</button>
@endifOr for a specific album:
@if ($user->can('blogs', ['album' => 'photos_2025']))
<button>Edit 2025 album blogs</button>
@endif| Requested | Granted | Has Access? |
|---|---|---|
| blogs?author_id=123 | blogs?author_id=123 | ✔️ |
| blogs?author_id=123 | blogs | ✔️ |
| blogs?author_id=123 | blogs?author_id=456 | ✖️ |
| blogs?album=photos_2025 | blogs | ✔️ |
| blogs?album=photos_2025 | blogs?album=photos_2025 | ✔️ |
| blogs?album=photos_2025 | blogs?album=photos_2024 | ✖️ |
Here’s an example of how you can assign permissions to roles in the view.config.json5 file:
{
"roles": {
// The marketer can manage and publish homepage and blog content
"marketer": {
"permissions": [
"homepage",
"blogs",
]
},
// The blogger can create new blogs and edit their own blogs
"blogger": {
"permissions": [
"blogs?author_id=me",
"blogs.create",
]
},
}
}You can add as many roles and permissions as you need for your project. You can also assign multiple roles to a single user.
In Confetti, you have one account for all your websites. That’s why permissions are always stored as a full path, starting with a / (slash) followed by the repository name (e.g., /ninja-agency/silent-site/blogs). This makes it possible to manage access across multiple projects and organizations with a single user account.
An owner of an agency can have a permission like /ninja-agency (instead of /ninja-agency/silent-site/). In that case, the owner is allowed to do everything within all repositories of the agency. Packages can still have their own restrictions (these are prefixed with their own repository, like /the-pkg-maker/image-uploader). See the package section for more information.
In some situations, you may want to parse a user's permission. For example, you can use this to show a list of all blogs the user has access to. You can parse a permission just like you would parse a URL. For example, in PHP:
$permission = '/ninja-agency/silent-site/blogs?author_id=123';
$parts = parse_url($permission);
// $parts['path'] will result in /ninja-agency/silent-site/blogs
parse_str($parts['query'], $queryParams);
// This will result in ['author_id' => '123']Because it is easy to create your own containers, it is good to know that you can also use the standard library in your favorite programming language to parse permissions. For example, in Go:
import (
"net/url"
"fmt"
)
func main() {
permission := "/ninja-agency/silent-site/blogs?author_id=123"
u, _ := url.Parse(permission)
fmt.Println(u.Path) // '/ninja-agency/silent-site/blogs'
fmt.Println(u.RawQuery) // 'author_id=123'
params, _ := url.ParseQuery(u.RawQuery)
fmt.Println(params.Get("author_id")) // '123'
}Sometimes you want to loop through all the permissions a user has access to. Here's an example of how you can do this in PHP:
$permissions = $user->can('blogs');
$categories = [];
foreach ($permissions as $permission) {
// Only loop over the blogs where the user has permission.
// For example, $permission could be 'blogs?category=photos_2025'.
$parts = parse_url($permission);
parse_str($parts['query'], $queryParams);
$categories = array_merge($$categories, $queryParams['category']);
}
// Now you can run a database query to find the blogs:
$blogs = Blogs::whereIn('category', $categories)->get();