Configure granular access control for Grav users using roles, groups, and custom permissions.
Permission System Overview
Grav uses a flexible ACL (Access Control List) system with:
- User-level permissions - Set directly on user accounts
- Group-based permissions - Inherit from user groups
- Page-level permissions - Control access to specific pages
- Admin permissions - Control admin panel access
Permission Structure
Access Levels
# user/accounts/username.yaml
access:
admin: # Admin panel permissions
login: true # Can access admin
super: true # Super admin (all permissions)
pages: true # Manage pages
plugins: true # Manage plugins
themes: true # Manage themes
users: true # Manage users
config: true # Edit configuration
site: # Frontend permissions
login: true # Can login to site
Predefined Permission Sets
Super Administrator
# user/accounts/superadmin.yaml
state: enabled
email: superadmin@example.com
fullname: Super Administrator
access:
admin:
login: true
super: true # Grants ALL permissions
site:
login: true
Administrator (Limited)
# user/accounts/admin.yaml
state: enabled
email: admin@example.com
fullname: Administrator
access:
admin:
login: true
super: false
pages: true
plugins: true
themes: true
users: true
config: true
site:
login: true
Content Editor
# user/accounts/editor.yaml
state: enabled
email: editor@example.com
fullname: Content Editor
access:
admin:
login: true
super: false
pages: true # Can edit pages
plugins: false # Cannot manage plugins
themes: false # Cannot change themes
users: false # Cannot manage users
config: false # Cannot edit config
site:
login: true
Author
# user/accounts/author.yaml
state: enabled
email: author@example.com
fullname: Author
access:
admin:
login: true
super: false
pages: true # Limited page access (own pages only)
site:
login: true
Subscriber (Frontend Only)
# user/accounts/subscriber.yaml
state: enabled
email: subscriber@example.com
fullname: Subscriber
access:
admin:
login: false # No admin access
site:
login: true # Frontend login only
User Groups
Creating Groups
# user/accounts/groups.yaml
editors:
readableName: Content Editors
description: Users who create and edit content
icon: fa-pencil
access:
admin:
login: true
pages: true
site:
login: true
authors:
readableName: Authors
description: Users who write blog posts
icon: fa-user-edit
access:
admin:
login: true
pages: true
site:
login: true
subscribers:
readableName: Subscribers
description: Frontend users with no admin access
icon: fa-users
access:
admin:
login: false
site:
login: true
Assigning Users to Groups
# user/accounts/johndoe.yaml
state: enabled
email: johndoe@example.com
fullname: John Doe
groups:
- editors # Member of editors group
# Inherits editors group permissions
# Plus individual permissions
access:
admin:
login: true
pages: true
site:
login: true
Multiple Group Assignment
# user/accounts/janedoe.yaml
state: enabled
email: janedoe@example.com
fullname: Jane Doe
groups:
- editors
- authors
# Inherits combined permissions from both groups
access:
admin:
login: true
pages: true
site:
login: true
Admin Panel Permissions
Specific Admin Permissions
access:
admin:
login: true # Access admin panel
super: false # Not super admin
# Content management
pages: true # Edit pages
pages.delete: false # Cannot delete pages
# Configuration
config: true # Edit site config
config.system: false # Cannot edit system config
config.site: true # Can edit site config
# Plugins
plugins: true # Manage plugins
plugins.install: false # Cannot install new plugins
# Themes
themes: true # Manage themes
themes.install: false # Cannot install new themes
# Users
users: true # Manage users
users.create: false # Cannot create users
users.delete: false # Cannot delete users
Read-Only Admin Access
# user/accounts/viewer.yaml
access:
admin:
login: true
super: false
pages: false # No page editing
config: false # No config editing
plugins: false # No plugin management
themes: false # No theme management
users: false # No user management
# Can view admin panel but cannot modify
Page-Level Permissions
Restrict Page Access
# pages/01.home/default.md
---
title: Home
access:
site.login: true # Requires login
admin.pages: true # Requires pages permission
---
Page content here...
Role-Based Page Access
# pages/members/default.md
---
title: Members Area
access:
site.login: true
groups:
- subscribers
- editors
---
Members only content...
Check Permissions in Templates
{# Check if user can access admin #}
{% if grav.user.authorize('admin.login') %}
<a href="/admin">Admin Panel</a>
{% endif %}
{# Check if user can edit pages #}
{% if grav.user.authorize('admin.pages') %}
<a href="/admin/pages">Edit Pages</a>
{% endif %}
{# Check if user is in group #}
{% if 'editors' in grav.user.groups %}
<a href="/editor-dashboard">Editor Dashboard</a>
{% endif %}
Custom Permissions
Define Custom Permissions
# user/accounts/johndoe.yaml
access:
admin:
login: true
site:
login: true
# Custom permissions
custom:
view_analytics: true
export_data: true
manage_comments: true
moderate_forum: false
Check Custom Permissions
{# Check custom permission #}
{% if grav.user.authorize('site.custom.view_analytics') %}
<a href="/analytics">View Analytics</a>
{% endif %}
{% if grav.user.authorize('site.custom.export_data') %}
<button>Export Data</button>
{% endif %}
Use in Plugins
<?php
// Check permission in plugin
if ($this->grav['user']->authorize('site.custom.view_analytics')) {
// Show analytics
} else {
// Deny access
return $this->grav->redirect('/');
}
Permission Inheritance
How Permissions Combine
- Group Permissions - Base permissions from groups
- User Permissions - Override/extend group permissions
- Page Permissions - Further restrict based on page
# Group: editors
access:
admin:
login: true
pages: true
# User: johndoe (in editors group)
groups:
- editors
access:
admin:
login: true
pages: true
config: true # Additional permission
# Effective permissions:
# - admin.login: true (from group)
# - admin.pages: true (from group)
# - admin.config: true (from user)
Protecting Content
Require Login for Entire Site
# user/config/plugins/login.yaml
route: /login
redirect_to_login: true
# Protect all pages by default
protect_protected_page_media: true
Public vs Protected Pages
# Public page (pages/about/default.md)
---
title: About
# No access restrictions
---
# Protected page (pages/dashboard/default.md)
---
title: Dashboard
access:
site.login: true
---
# Admin only page (pages/admin-area/default.md)
---
title: Admin Area
access:
admin.login: true
---
Conditional Content Display
{# Show content based on permissions #}
{% if grav.user.authenticated %}
<p>Welcome, {{ grav.user.fullname }}!</p>
{% if grav.user.authorize('admin.login') %}
<p>You have admin access.</p>
{% endif %}
{% if 'editors' in grav.user.groups %}
<p>You are an editor.</p>
{% endif %}
{% else %}
<p><a href="/login">Please login</a></p>
{% endif %}
Role-Based Redirects
Redirect After Login Based on Role
# user/config/plugins/login.yaml
redirect_after_login:
default: '/'
# Custom redirect per group
redirect_after_login_by_role:
admin.super: '/admin'
admin.pages: '/admin/pages'
site.login: '/dashboard'
Twig-Based Redirects
{# Redirect based on user role #}
{% if grav.user.authenticated %}
{% if grav.user.authorize('admin.super') %}
{% do grav.redirect('/admin') %}
{% elseif grav.user.authorize('admin.pages') %}
{% do grav.redirect('/admin/pages') %}
{% else %}
{% do grav.redirect('/dashboard') %}
{% endif %}
{% endif %}
Best Practices
1. Principle of Least Privilege
Give users minimum permissions needed:
# Bad - too many permissions
access:
admin:
super: true # Everything
# Good - specific permissions
access:
admin:
login: true
pages: true # Only what's needed
2. Use Groups for Common Roles
# Define groups for common roles
# user/accounts/groups.yaml
editors:
access:
admin:
login: true
pages: true
# Assign users to groups
# user/accounts/editor1.yaml
groups:
- editors
3. Document Custom Permissions
# user/accounts/groups.yaml
# Document what each permission does
moderators:
description: Users who can moderate content
access:
custom:
moderate_comments: true # Can approve/delete comments
view_reports: true # Can view user reports
ban_users: false # Cannot ban users
4. Regular Permission Audits
# List all users and their permissions
for file in user/accounts/*.yaml; do
echo "=== $file ==="
grep -A 10 "access:" $file
done
5. Separate Admin and Content Users
# Content editors - limited admin access
access:
admin:
login: true
pages: true
# System admins - full access
access:
admin:
super: true
Troubleshooting
User Has Access But Cannot Edit
Check:
- User has
admin.pagespermission - Page is not protected with stricter permissions
- User state is
enabled - Cache cleared after permission change
Permission Changes Not Taking Effect
# Clear cache
bin/grav clear-cache
# Logout and login again
# Permissions cached in session
Cannot Access Admin Panel
Verify:
access:
admin:
login: true # Must be true
Next Steps
- Adding/Removing Users - User management
- Login Plugin - Authentication system
- Admin Panel - Admin interface