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
Section titled “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
Section titled “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
Section titled “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
Section titled “Permission Types”Collections
Section titled “Collections”Collections have two separate permission levels:
Collection Metadata (collectionsMeta)
Section titled “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)
Section titled “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
Section titled “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
Section titled “Templates”"templates": true // or falseSimple boolean for full access or no access to templates. Templates don’t have granular CRUD permissions.
Settings
Section titled “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": { "all": false, "allowed": ["cache-manager", "jumpstart"]}all- Iftrue, access all utility pagesallowed- Array of specific utility page names
Boolean Permissions
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “Templates”Check templates access (boolean):
{% if cms.canAccessTemplates() %} <a href="/admin/templates">Templates</a>{% endif %}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
Section titled “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
Section titled “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
Section titled “Practical Examples”Conditional Navigation Menu
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “Common Access Group Configurations”Full Admin
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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 |