Access Groups
Access groups provide fine-grained permission control for Total CMS, allowing you to restrict what users can access and modify in both the admin dashboard and via the REST API.
Overview
Access groups work at two levels:
- Middleware Enforcement: Routes are protected by middleware that checks permissions before allowing access
- UI Controls: Template helper functions hide/show UI elements based on permissions
Super admin users (members of the admin group in the default auth collection) bypass all access checks automatically.
Access Group Structure
Access groups are defined in tcms-data/.system/access-groups.json:
{
"groups": [
{
"id": "editor",
"description": "Blog and news editor",
"permissions": {
"collectionsMeta": {
"operations": ["read"],
"all": false,
"allowed": ["blog", "news"]
},
"collections": {
"operations": ["create", "read", "update", "delete"],
"all": false,
"allowed": ["blog", "news"]
},
"schemas": {
"operations": ["read"],
"all": false,
"allowed": ["blog", "news"]
},
"templates": true,
"mailer": false,
"playground": true,
"docs": true,
"utils": {
"all": false,
"allowed": ["cache-manager"]
},
"settings": {
"all": false,
"allowed": []
}
}
}
]
}
CRUD Operations
Total CMS uses CRUD (Create, Read, Update, Delete) operations for fine-grained permission control:
create- Create new resources/objectsread- View/list/fetch resourcesupdate- Modify existing resourcesdelete- Delete/remove resources
These operations replace the older HTTP method approach and provide clearer, more intuitive permission control.
Permission Types
Collections
Collections have two separate permission levels:
Collection Metadata (collectionsMeta)
Controls access to managing collection definitions (creating/editing/deleting collections themselves):
"collectionsMeta": {
"operations": ["read", "update"],
"all": false,
"allowed": ["blog", "news"]
}
create- Create new collectionsread- View collection definitionsupdate- Edit collection settingsdelete- Delete collections
Collection Objects (collections)
Controls access to objects within collections:
"collections": {
"operations": ["create", "read", "update", "delete"],
"all": true,
"allowed": []
}
create- Create new objectsread- View/list objectsupdate- Edit objectsdelete- Delete objectsall- Iftrue, access all collections; iffalse, only those inallowedallowed- Array of specific collection IDs
Schemas
"schemas": {
"operations": ["create", "read", "update", "delete"],
"all": false,
"allowed": ["blog", "product"]
}
create- Create new schemasread- View schemasupdate- Edit schemasdelete- Delete schemasall- Iftrue, access all schemas; iffalse, only those inallowedallowed- Array of specific schema names
Templates
"templates": true // or false
Simple boolean for full access or no access to templates. Templates don't have granular CRUD permissions.
Settings
"settings": {
"all": false,
"allowed": ["general", "cache"]
}
all- Iftrue, access all settings sectionsallowed- Array of specific setting section names (e.g., "general", "cache", "auth", "mailer")
Utils
"utils": {
"all": false,
"allowed": ["cache-manager", "jumpstart"]
}
all- Iftrue, access all utility pagesallowed- Array of specific utility page names
Boolean Permissions
Simple true/false for features without granular control:
mailer- Access to mailer/email functionalityplayground- Access to Twig playgrounddocs- Access to documentation
Twig Helper Functions
Total CMS provides helper functions to check permissions in your templates, allowing you to hide/show UI elements based on the current user's access groups.
Collections
Check specific collection object access:
{% if cms.canAccessCollection('blog', 'read') %}
<a href="/admin/collections/blog">View Blog Posts</a>
{% endif %}
{% if cms.canAccessCollection('blog', 'create') %}
<a href="/admin/collections/blog/new">New Post</a>
{% endif %}
{% if cms.canAccessCollection('blog', 'update') %}
<button>Edit Post</button>
{% endif %}
{% if cms.canAccessCollection('blog', 'delete') %}
<button class="delete">Delete Post</button>
{% endif %}
Check general collection object access:
{% if cms.canAccessCollectionsOperation('read') %}
<p>You can view collections</p>
{% endif %}
Check collection metadata access:
{% if cms.canAccessCollectionMeta('blog', 'read') %}
<a href="/admin/collections/blog/settings">View Collection Settings</a>
{% endif %}
{% if cms.canAccessCollectionMeta('blog', 'update') %}
<button>Edit Collection Settings</button>
{% endif %}
{% if cms.canAccessCollectionMeta('blog', 'delete') %}
<button class="delete">Delete Collection</button>
{% endif %}
Check general collection metadata access:
{% if cms.canAccessCollectionsMetaOperation('read') %}
<a href="/admin/collections">View Collections List</a>
{% endif %}
{% if cms.canAccessCollectionsMetaOperation('create') %}
<a href="/admin/collections/new">New Collection</a>
{% endif %}
Get list of accessible collections:
{% for collection in cms.getAccessibleCollections('read') %}
<li>{{ collection }}</li>
{% endfor %}
Schemas
Check specific schema access:
{% if cms.canAccessSchema('blog', 'read') %}
<a href="/admin/schemas/blog">View Blog Schema</a>
{% endif %}
{% if cms.canAccessSchema('blog', 'update') %}
<a href="/admin/schemas/blog/edit">Edit Schema</a>
{% endif %}
{% if cms.canAccessSchema('blog', 'delete') %}
<button class="delete">Delete Schema</button>
{% endif %}
Check general schemas access:
{% if cms.canAccessSchemasOperation('read') %}
<a href="/admin/schemas">View Schemas</a>
{% endif %}
{% if cms.canAccessSchemasOperation('create') %}
<a href="/admin/schemas/new">New Schema</a>
{% endif %}
Templates
Check templates access (boolean):
{% if cms.canAccessTemplates() %}
<a href="/admin/templates">Templates</a>
{% endif %}
Utils
Check specific utils page:
{% if cms.canAccessUtil('cache-manager') %}
<a href="/admin/utils/cache-manager">Cache Manager</a>
{% endif %}
{% if cms.canAccessUtil('jumpstart') %}
<a href="/admin/utils/jumpstart">JumpStart</a>
{% endif %}
Check general utils access:
{% if cms.canAccessUtils() %}
<a href="/admin/utils">Utils</a>
{% endif %}
Boolean Permissions
Check mailer access:
{% if cms.canAccessMailer() %}
<a href="/admin/mailer">Mailer</a>
{% endif %}
Check playground access:
{% if cms.canAccessPlayground() %}
<a href="/admin/utils/twig-playground">Twig Playground</a>
{% endif %}
Check docs access:
{% if cms.canAccessDocs() %}
<a href="/admin/docs">Documentation</a>
{% endif %}
Admin Check
Check if user is super admin:
{% if cms.isAdmin() %}
<div class="admin-only-feature">
<a href="/admin/utils/access-groups">Manage Access Groups</a>
</div>
{% endif %}
Super admins bypass all access checks and have full access to everything.
Practical Examples
Conditional Navigation Menu
<nav>
{% if cms.canAccessCollectionsMetaOperation('read') %}
<a href="/admin/collections">Collections</a>
{% endif %}
{% if cms.canAccessSchemasOperation('read') %}
<a href="/admin/schemas">Schemas</a>
{% endif %}
{% if cms.canAccessTemplates() %}
<a href="/admin/templates">Templates</a>
{% endif %}
{% if cms.canAccessUtils() %}
<a href="/admin/utils">Utils</a>
{% endif %}
{% if cms.isAdmin() %}
<a href="/admin/utils/access-groups">Access Groups</a>
{% endif %}
</nav>
Filtered Collection List
<ul>
{% for collection in collections %}
{% if cms.canAccessCollection(collection.id, 'read') %}
<li>
<a href="/admin/collections/{{ collection.id }}">{{ collection.name }}</a>
{% if cms.canAccessCollection(collection.id, 'create') %}
<a href="/admin/collections/{{ collection.id }}/new">New Item</a>
{% endif %}
{% if cms.canAccessCollectionMeta(collection.id, 'update') %}
<a href="/admin/collections/{{ collection.id }}/settings">Settings</a>
{% endif %}
</li>
{% endif %}
{% endfor %}
</ul>
Collection Object Actions
<div class="object-actions">
{% if cms.canAccessCollection(collection, 'create') %}
<a href="/admin/collections/{{ collection }}/new" class="btn">New Object</a>
{% endif %}
{% if cms.canAccessCollection(collection, 'update') %}
<button class="btn" data-action="edit">Edit</button>
{% endif %}
{% if cms.canAccessCollection(collection, 'delete') %}
<button class="btn btn-danger" data-action="delete">Delete</button>
{% endif %}
</div>
Schema Management Buttons
<div class="schema-actions">
{% if cms.canAccessSchemasOperation('create') %}
<a href="/admin/schemas/new" class="btn">New Schema</a>
{% endif %}
{% if cms.canAccessSchema(schema.id, 'update') %}
<a href="/admin/schemas/{{ schema.id }}/edit" class="btn">Edit</a>
{% endif %}
{% if cms.canAccessSchema(schema.id, 'delete') %}
<button class="btn btn-danger" data-schema="{{ schema.id }}">Delete</button>
{% endif %}
</div>
Common Access Group Configurations
Full Admin
{
"id": "admin",
"description": "Full administrative access",
"permissions": {
"collectionsMeta": {
"operations": ["create", "read", "update", "delete"],
"all": true,
"allowed": []
},
"collections": {
"operations": ["create", "read", "update", "delete"],
"all": true,
"allowed": []
},
"schemas": {
"operations": ["create", "read", "update", "delete"],
"all": true,
"allowed": []
},
"templates": true,
"mailer": true,
"playground": true,
"docs": true,
"utils": {
"all": true,
"allowed": []
},
"settings": {
"all": true,
"allowed": []
}
}
}
Content Editor
{
"id": "editor",
"description": "Full CRUD access to specific collections",
"permissions": {
"collectionsMeta": {
"operations": ["read"],
"all": false,
"allowed": ["blog", "news"]
},
"collections": {
"operations": ["create", "read", "update", "delete"],
"all": false,
"allowed": ["blog", "news"]
},
"schemas": {
"operations": ["read"],
"all": false,
"allowed": ["blog", "news"]
},
"templates": false,
"mailer": false,
"playground": true,
"docs": true,
"utils": {
"all": false,
"allowed": []
},
"settings": {
"all": false,
"allowed": []
}
}
}
Read-Only Viewer
{
"id": "viewer",
"description": "Read-only access to all collections",
"permissions": {
"collectionsMeta": {
"operations": ["read"],
"all": true,
"allowed": []
},
"collections": {
"operations": ["read"],
"all": true,
"allowed": []
},
"schemas": {
"operations": ["read"],
"all": true,
"allowed": []
},
"templates": true,
"mailer": false,
"playground": true,
"docs": true,
"utils": {
"all": false,
"allowed": []
},
"settings": {
"all": false,
"allowed": []
}
}
}
Single Collection Specialist
{
"id": "blogger",
"description": "Full CRUD access to blog collection only",
"permissions": {
"collectionsMeta": {
"operations": ["read"],
"all": false,
"allowed": ["blog"]
},
"collections": {
"operations": ["create", "read", "update", "delete"],
"all": false,
"allowed": ["blog"]
},
"schemas": {
"operations": ["read"],
"all": false,
"allowed": ["blog"]
},
"templates": false,
"mailer": false,
"playground": true,
"docs": true,
"utils": {
"all": false,
"allowed": []
},
"settings": {
"all": false,
"allowed": []
}
}
}
Best Practices
-
Use UI Controls Everywhere: Always check permissions before showing links, buttons, or forms. Don't rely on middleware alone - good UX means hiding what users can't access.
-
Check Appropriate Operations: Use the appropriate CRUD operation for the action:
readfor viewing/listingcreatefor creating new resourcesupdatefor modifying existing resourcesdeletefor deleting resources
-
Distinguish Collections from Collection Metadata:
- Use
canAccessCollection()for working with objects within a collection - Use
canAccessCollectionMeta()for managing collection definitions
- Use
-
Graceful Degradation: Hide buttons/actions users can't perform rather than showing disabled buttons.
-
Admin-Only Features: Use
cms.isAdmin()for features that should never be delegated (like managing access groups, API keys, or user accounts). -
Operation-Level Checks: Use general operation checks (e.g.,
canAccessCollectionsOperation()) for listing pages where no specific resource is selected yet. -
Principle of Least Privilege: Grant users only the minimum permissions they need to accomplish their tasks.
Admin-Only Routes
Some routes require super admin access and cannot be delegated via access groups:
- Access Groups Management:
/admin/utils/access-groups - API Key Management:
/admin/apikeys/* - User Management:
/admin/users/*
These routes use AdminOnlyMiddleware which only allows super admin users.
CRUD Operations Reference
| Operation | Purpose | Example Use Case |
|---|---|---|
create |
Create new resources | Add new blog post, create collection |
read |
View/list resources | View blog posts, list schemas |
update |
Modify existing resources | Edit blog post, update collection settings |
delete |
Remove resources | Delete blog post, remove collection |