Roles and Permissions
Roles and permissions aren’t just for security nerds. If you want to know who can do what, and why your API key sometimes says "nope," you’re in the right place. They’re your best friends when you want the user to:
- 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 and further modify the page
You assign permissions to roles, and then hand out those roles to your users.
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
blog
can create and edit the entire blog, including all fields. - An SEO specialist with Granted Permission
blog/title
can 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 extension 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>
@endif
This 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>
@endif
Or 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 a roles.json5
file:
{
// 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'] = '/ninja-agency/silent-site/blogs'
// $parts['query'] = 'author_id=123'
parse_str($parts['query'], $queryParams);
// $queryParams = ['author_id' => '123']
If you are building your own package in Go, you can use the standard library:
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. For example, to show a list of all blogs the user can edit:
$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();