Skip to main content

Permission Definitions

The template includes a fine-grained permission system organized into resource-based definitions, UI-friendly groups, and state management utilities. This system controls access to admin features, content management, and analytics.

File Structure

lib/permissions/
definitions.ts # Permission constants, types, default roles
groups.ts # UI-oriented permission groups, formatting helpers
utils.ts # State management utilities for permission UIs

Permission Definitions (definitions.ts)

All permissions follow a resource:action naming convention. The PERMISSIONS object is the single source of truth:

export const PERMISSIONS = {
items: {
read: 'items:read',
create: 'items:create',
update: 'items:update',
delete: 'items:delete',
review: 'items:review',
approve: 'items:approve',
reject: 'items:reject',
},
categories: {
read: 'categories:read',
create: 'categories:create',
update: 'categories:update',
delete: 'categories:delete',
},
tags: {
read: 'tags:read',
create: 'tags:create',
update: 'tags:update',
delete: 'tags:delete',
},
roles: {
read: 'roles:read',
create: 'roles:create',
update: 'roles:update',
delete: 'roles:delete',
},
users: {
read: 'users:read',
create: 'users:create',
update: 'users:update',
delete: 'users:delete',
assignRoles: 'users:assignRoles',
},
analytics: {
read: 'analytics:read',
export: 'analytics:export',
},
system: {
settings: 'system:settings',
},
} as const;

Permission Type

The Permission type is automatically derived from the PERMISSIONS object, ensuring type safety without manual duplication:

type PermissionValues<T> = T extends Record<string, infer U>
? U extends Record<string, infer V>
? V extends string ? V : never
: never
: never;

export type Permission = PermissionValues<typeof PERMISSIONS>;
// Results in: 'items:read' | 'items:create' | ... | 'system:settings'

Utility Functions

// Get all permissions as a flat array
getAllPermissions(): Permission[]

// Get permissions for a specific resource
getPermissionsForResource(resource: keyof typeof PERMISSIONS): Permission[]

// Type guard to validate a string is a valid permission
isValidPermission(permission: string): permission is Permission

Usage example:

import { getAllPermissions, getPermissionsForResource, isValidPermission } from '@/lib/permissions/definitions';

const allPerms = getAllPermissions();
// => ['items:read', 'items:create', ..., 'system:settings']

const itemPerms = getPermissionsForResource('items');
// => ['items:read', 'items:create', 'items:update', ...]

if (isValidPermission(userInput)) {
// userInput is typed as Permission
}

Default Roles

Two built-in role definitions provide sensible defaults:

export const DEFAULT_ROLES = {
SUPER_ADMIN: {
id: 'super-admin',
name: 'Super Administrator',
description: 'Full system access with all permissions',
permissions: getAllPermissions(),
},
CONTENT_MANAGER: {
id: 'content-manager',
name: 'Content Manager',
description: 'Manage content including items, categories, and tags',
permissions: [
...getPermissionsForResource('items'),
...getPermissionsForResource('categories'),
...getPermissionsForResource('tags'),
],
},
} as const;

Permission Groups (groups.ts)

Permission groups organize permissions for display in admin UI components such as role editors:

export interface PermissionGroup {
id: string;
name: string;
description: string;
icon: string; // Lucide icon name
permissions: Permission[];
}

export const PERMISSION_GROUPS: PermissionGroup[] = [
{
id: 'content',
name: 'Content Management',
description: 'Manage items, categories, and tags',
icon: 'FileText',
permissions: [
...getPermissionsForResource('items'),
...getPermissionsForResource('categories'),
...getPermissionsForResource('tags'),
],
},
{
id: 'users',
name: 'User Management',
description: 'Manage users and their roles',
icon: 'Users',
permissions: [
...getPermissionsForResource('users'),
...getPermissionsForResource('roles'),
],
},
{
id: 'system',
name: 'System & Analytics',
description: 'System settings and analytics access',
icon: 'Settings',
permissions: [
...getPermissionsForResource('analytics'),
...getPermissionsForResource('system'),
],
},
];

Group Utility Functions

// Find which group a permission belongs to
getPermissionGroup(permission: Permission): PermissionGroup | undefined

// Get all permissions in a group by ID
getPermissionsByGroup(groupId: string): Permission[]

// Format "items:read" into "Read Items"
formatPermissionName(permission: Permission): string

// Format "items:read" into "View and access items and submissions"
formatPermissionDescription(permission: Permission): string

Usage example:

import {
getPermissionGroup,
formatPermissionName,
formatPermissionDescription,
} from '@/lib/permissions/groups';

const group = getPermissionGroup('items:read');
// => { id: 'content', name: 'Content Management', ... }

formatPermissionName('items:approve');
// => "Approve Items"

formatPermissionDescription('users:assignRoles');
// => "Assign roles to users"

Permission State Utilities (utils.ts)

These functions manage permission toggle state in admin UIs (such as role creation or editing forms):

Creating Permission State

Convert a permissions array into a boolean map for checkbox-based UIs:

import { createPermissionState, getSelectedPermissions } from '@/lib/permissions/utils';

const currentPerms: Permission[] = ['items:read', 'items:create'];

const state = createPermissionState(currentPerms);
// => { 'items:read': true, 'items:create': true }

// After user toggles checkboxes...
state['items:update'] = true;
state['items:read'] = false;

const selected = getSelectedPermissions(state);
// => ['items:create', 'items:update']

Calculating Changes

Determine what was added and removed when comparing permission sets:

import { calculatePermissionChanges } from '@/lib/permissions/utils';

const original = ['items:read', 'items:create'];
const updated = ['items:read', 'items:update'];

const changes = calculatePermissionChanges(original, updated);
// => { added: ['items:update'], removed: ['items:create'] }

Comparing Permissions

Check if two permission arrays contain the same permissions regardless of order:

import { arePermissionsEqual } from '@/lib/permissions/utils';

arePermissionsEqual(['items:read', 'items:create'], ['items:create', 'items:read']);
// => true

Filtering Permissions

Search through permissions by keyword:

import { filterPermissions } from '@/lib/permissions/utils';

const allPerms = getAllPermissions();
const results = filterPermissions(allPerms, 'delete');
// => ['items:delete', 'categories:delete', 'tags:delete', ...]

Common Patterns

Checking Permissions in a Component

import { PERMISSIONS } from '@/lib/permissions/definitions';

function canUserApprove(userPermissions: string[]): boolean {
return userPermissions.includes(PERMISSIONS.items.approve);
}

Building a Role Editor

import { PERMISSION_GROUPS } from '@/lib/permissions/groups';
import { createPermissionState, getSelectedPermissions } from '@/lib/permissions/utils';

function RoleEditor({ role }) {
const [permState, setPermState] = useState(
createPermissionState(role.permissions)
);

// Render groups with toggleable checkboxes
return PERMISSION_GROUPS.map(group => (
<PermissionGroupCard key={group.id} group={group} state={permState} />
));
}
  • lib/permissions/definitions.ts - Permission constants, types, and default roles
  • lib/permissions/groups.ts - UI groups and formatting helpers
  • lib/permissions/utils.ts - State management utilities
  • lib/guards/ - Route and component guards that consume permissions