Skip to main content

ACL Protection Rules

Document Metadata
FieldValue
Version1.0.0
Last UpdatedJanuary 15, 2025
OwnerFrontend Team
Status✅ Current
TypeStandard
Important

ACL protection is currently only for Whitelabel modules. Before implementing a new module or adding actions, confirm whether ACL is required.

Rationale​

Access Control List (ACL) protection ensures that users can only access and modify resources they have permission to interact with. This standard provides consistent patterns for implementing ACL across all Whitelabel modules, ensuring:

  • Security: Unauthorized users cannot access or modify protected resources
  • Consistency: All modules follow the same protection patterns
  • Maintainability: Clear, predictable implementation patterns make code easier to understand and maintain
  • User Experience: Users see appropriate UI elements based on their permissions

Permission Format​

Protection TypeFormatExample
Route protectionview:{resource}view:articles, view:branches
Action protectioncontrol:{resource}control:articles, control:branches

Protection Patterns​

1. Route Protection​

Add aclGrants to the route definition in pathEntry.tsx:

aclProps: { aclGrants: ['view:articles'] }

2. Direct Action Buttons (Delete, Publish, etc.)​

Behavior: Hide the button if user doesn't have control:{resource} permission.

Preferred: ACLBoundary Component​

<ACLBoundary
aclGrants={['control:articles']}
aclUnAuthProps={{ hide: true }}
>
<Button onClick={handleDelete}>Delete</Button>
</ACLBoundary>

Alternative: useACL Hook​

Only use when hiding with ACL is complex (e.g., complex conditional logic):

const { isAllowed } = useACL(['control:articles']);
if (!isAllowed) return null;
return <Button onClick={handleDelete}>Delete</Button>;

3. Edit Actions (Open Drawer)​

Behavior:

  • ✅ Always show the edit button (users can view data)
  • ✅ Drawer opens normally and prefills data
  • ❌ Hide submit button if user doesn't have control:{resource} permission
return (
<Drawer>
<Drawer.Content>...</Drawer.Content>
<ACLBoundary
aclGrants={['control:articles']}
aclUnAuthProps={{ hide: true }}
>
<Drawer.Footer onSubmit={handleSubmit} />
</ACLBoundary>
</Drawer>
);

Alternative: useACL Hook​

const { isAllowed } = useACL(['control:articles']);
return (
<Drawer>
<Drawer.Content>...</Drawer.Content>
{isAllowed && <Drawer.Footer onSubmit={handleSubmit} />}
</Drawer>
);

4. Add/Create Buttons​

Behavior: Hide the button if user doesn't have control:{resource} permission.

Preferred: ACLBoundary Component​

<ACLBoundary
aclGrants={['control:articles']}
aclUnAuthProps={{ hide: true }}
>
<AddButton onClick={handleAdd} />
</ACLBoundary>

Alternative: useACL Hook​

const { isAllowed } = useACL(['control:articles']);
if (!isAllowed) return null;
return <AddButton onClick={handleAdd} />;

5. Page Headers​

Behavior: If the header only contains an Add/Create button and it's hidden, hide the entire header.

Check permission at header level:

const { isAllowed } = useACL(['control:articles']);
if (!isAllowed) return null;
return <PageHeader><AddButton onClick={handleAdd} /></PageHeader>;

6. EmptyState Actions​

Behavior:

  • ✅ Always show the EmptyState component (title, subtitle, etc.)
  • ❌ Hide only the action button if user doesn't have control:{resource} permission
Important

Do NOT wrap the entire EmptyState with ACLBoundary — this would hide the entire empty state.

Correct Implementation​

const { isAllowed: hasControlPermission } = useACL(['control:articles']);

return (
<EmptyState
title="No articles"
subtitle="Get started by adding your first article"
{...(hasControlPermission && {
action: {
onClick: handleAdd,
label: 'Add Article',
},
})}
data={articles}
/>
);

7. Inputs/Form Controls​

Behavior: Always use hide: true, never disabled: true.

<ACLBoundary
aclGrants={['control:articles']}
aclUnAuthProps={{ hide: true }}
>
<Input {...props} />
</ACLBoundary>

Implementation Checklist​

For each module, verify:

  • Route protected with view:{resource}
  • Delete buttons hidden (ACLBoundary preferred)
  • Edit buttons always visible
  • Drawer submit buttons hidden (ACLBoundary preferred)
  • Add buttons hidden (ACLBoundary preferred)
  • Page headers hidden if only Add button and no permission
  • EmptyState always visible, action button hidden via useACL hook

Quick Reference Table​

ElementVisibilityProtection Method
RouteBased on view:{resource}aclGrants in route config
Delete ButtonHidden without permissionACLBoundary (preferred)
Edit ButtonAlways visibleNone
Drawer SubmitHidden without permissionACLBoundary (preferred)
Add ButtonHidden without permissionACLBoundary (preferred)
Header (Add only)Hidden without permissionuseACL hook
EmptyStateAlways visible—
EmptyState ActionHidden without permissionuseACL hook
Form InputsHidden without permissionACLBoundary with hide: true

When to Use Each Approach​

Use ACLBoundary When:​

  • Protecting JSX elements directly
  • Simple show/hide logic
  • Cleaner, more declarative code is preferred

Use useACL Hook When:​

  • Complex conditional logic is required
  • Working with dynamic arrays
  • Conditionally including object properties (like EmptyState actions)
  • Need to check permission in non-JSX code

Important Notes​

Default Behavior

Always use hide: true in aclUnAuthProps to hide elements. Never use disabled: true for ACL-based protection.

Inputs

For form inputs protected by ACL, always use hide: true — disabled inputs create confusion about why data can't be edited.


Change Log​

VersionDateChangesAuthor
1.0.02025-01-15Initial versionFrontend Team