Wildcards
Wildcards provide flexible and powerful permission patterns.
What are Wildcards?
Wildcards use the * symbol to match multiple permissions with a single pattern. Instead of granting each permission individually, you can use wildcards to grant groups of permissions.
typescript
// Without wildcards - tedious
rbac.createRole('admin', [
'posts:read',
'posts:write',
'posts:delete',
'posts:publish',
'posts:archive'
// ... many more
])
// With wildcards - clean
rbac.createRole('admin', ['posts:*'])Wildcard Patterns
Resource Wildcard
Grant all actions on a specific resource:
typescript
rbac.createRole('post-admin', ['posts:*'])
const admin = { id: '1', roles: ['post-admin'] }
// All post actions are allowed
rbac.hasPermission(admin, 'posts:read') // ✅ true
rbac.hasPermission(admin, 'posts:write') // ✅ true
rbac.hasPermission(admin, 'posts:delete') // ✅ true
rbac.hasPermission(admin, 'posts:anything') // ✅ trueAction Wildcard
Grant specific action across all resources:
typescript
rbac.createRole('reader', ['*:read'])
const reader = { id: '1', roles: ['reader'] }
// Can read all resources
rbac.hasPermission(reader, 'posts:read') // ✅ true
rbac.hasPermission(reader, 'comments:read') // ✅ true
rbac.hasPermission(reader, 'users:read') // ✅ true
// Cannot perform other actions
rbac.hasPermission(reader, 'posts:write') // ❌ falseFull Wildcard
Grant all permissions (superuser):
typescript
rbac.createRole('superuser', ['*'])
const superuser = { id: '1', roles: ['superuser'] }
// Everything is allowed
rbac.hasPermission(superuser, 'posts:read') // ✅ true
rbac.hasPermission(superuser, 'posts:delete') // ✅ true
rbac.hasPermission(superuser, 'users:manage') // ✅ true
rbac.hasPermission(superuser, 'anything:goes') // ✅ trueNested Wildcards
Use wildcards with hierarchical permissions:
typescript
rbac.createRole('admin', ['admin:*'])
const admin = { id: '1', roles: ['admin'] }
// All admin actions are allowed
rbac.hasPermission(admin, 'admin:users:read') // ✅ true
rbac.hasPermission(admin, 'admin:users:write') // ✅ true
rbac.hasPermission(admin, 'admin:settings:write') // ✅ true
rbac.hasPermission(admin, 'admin:logs:view') // ✅ true
// Non-admin actions are not allowed
rbac.hasPermission(admin, 'posts:read') // ❌ falseWildcard Matching Rules
Exact Match Priority
Explicit permissions take priority over wildcards:
typescript
rbac.createRole('editor', [
'posts:*',
'!posts:delete' // Explicitly deny delete
])
// Note: Explicit denials are handled at application level
// Fire Shield returns true for posts:delete due to posts:*
// You need to check denials separatelyPartial Wildcards
Wildcards only match the segment they're in:
typescript
rbac.createRole('role', ['admin:users:*'])
const user = { id: '1', roles: ['role'] }
// Matches within admin:users namespace
rbac.hasPermission(user, 'admin:users:read') // ✅ true
rbac.hasPermission(user, 'admin:users:write') // ✅ true
// Doesn't match other namespaces
rbac.hasPermission(user, 'admin:settings:read') // ❌ false
rbac.hasPermission(user, 'users:read') // ❌ falseCommon Patterns
Admin Roles
typescript
// Full system admin
rbac.createRole('system-admin', ['*'])
// Module-specific admins
rbac.createRole('user-admin', ['users:*'])
rbac.createRole('content-admin', ['posts:*', 'comments:*'])
rbac.createRole('billing-admin', ['billing:*', 'invoices:*'])Read-Only Access
typescript
// Read everything
rbac.createRole('auditor', ['*:read'])
// Read specific modules
rbac.createRole('reporter', [
'analytics:*:read',
'reports:*:read',
'logs:read'
])Tiered Access
typescript
// Basic tier - limited access
rbac.createRole('basic', [
'app:read',
'profile:write:own'
])
// Pro tier - more features
rbac.createRole('pro', [
'app:read',
'app:advanced:*',
'profile:*',
'export:*'
])
// Enterprise tier - full access
rbac.createRole('enterprise', ['*'])Combining Wildcards and Specific Permissions
Mix wildcards with specific permissions:
typescript
rbac.createRole('moderator', [
'posts:read',
'posts:write',
'comments:*', // All comment actions
'users:read'
])
const moderator = { id: '1', roles: ['moderator'] }
// Specific post permissions
rbac.hasPermission(moderator, 'posts:read') // ✅ true
rbac.hasPermission(moderator, 'posts:write') // ✅ true
rbac.hasPermission(moderator, 'posts:delete') // ❌ false
// All comment permissions
rbac.hasPermission(moderator, 'comments:read') // ✅ true
rbac.hasPermission(moderator, 'comments:moderate') // ✅ true
rbac.hasPermission(moderator, 'comments:delete') // ✅ truePerformance Considerations
Wildcards are highly optimized:
typescript
// ✅ Efficient - single wildcard
rbac.createRole('admin', ['posts:*'])
// ✅ Also efficient - few specific permissions
rbac.createRole('editor', [
'posts:read',
'posts:write',
'posts:publish'
])
// ⚠️ Consider wildcard - many similar permissions
rbac.createRole('admin', [
'posts:read',
'posts:write',
'posts:delete',
'posts:publish',
'posts:archive',
'posts:restore',
// ... 20 more post permissions
])
// Better: rbac.createRole('admin', ['posts:*'])Best Practices
1. Use Wildcards for Admin Roles
typescript
// ✅ Good - clear admin intent
rbac.createRole('admin', ['posts:*'])
// ❌ Avoid - tedious and error-prone
rbac.createRole('admin', [
'posts:read',
'posts:write',
// ... might miss some permissions
])2. Be Specific When Needed
typescript
// ✅ Good - specific access
rbac.createRole('editor', [
'posts:read',
'posts:write',
'posts:publish'
// Deliberately excludes posts:delete
])
// ❌ Avoid - too broad
rbac.createRole('editor', ['posts:*'])
// Includes delete, which editors shouldn't have3. Document Wildcard Usage
typescript
// ✅ Good - documented
rbac.createRole('content-moderator', [
'posts:*', // Full post management
'comments:*' // Full comment management
// Note: Cannot manage users
])
// ❌ Avoid - unclear scope
rbac.createRole('moderator', ['*'])
// What does this role actually do?4. Combine with Role Hierarchy
typescript
// ✅ Good - clear progression
rbac.createRole('viewer', ['posts:read'])
rbac.createRole('editor', ['posts:read', 'posts:write'])
rbac.createRole('admin', ['posts:*'])
rbac.setRoleHierarchy({
admin: ['editor'],
editor: ['viewer']
})Advanced Patterns
Namespace-Based Access
typescript
// Organize permissions by namespace
rbac.createRole('api-full', ['api:*'])
rbac.createRole('api-read', ['api:*:read'])
const apiUser = { id: '1', roles: ['api-full'] }
rbac.hasPermission(apiUser, 'api:v1:read') // ✅ true
rbac.hasPermission(apiUser, 'api:v2:write') // ✅ trueMulti-Tenant Wildcards
typescript
// Tenant-scoped permissions
rbac.createRole('tenant-admin', [
'tenant:*', // All tenant operations
'tenant:users:*', // All tenant user operations
'tenant:billing:read' // Can only read billing
])Feature Flags
typescript
// Feature-based permissions
rbac.createRole('beta-tester', [
'app:*',
'features:beta:*', // All beta features
'features:experimental:read' // Can view experimental features
])Testing Wildcards
Always test wildcard permissions thoroughly:
typescript
describe('Admin permissions', () => {
it('should allow all post actions', () => {
rbac.createRole('admin', ['posts:*'])
const admin = { id: '1', roles: ['admin'] }
expect(rbac.hasPermission(admin, 'posts:read')).toBe(true)
expect(rbac.hasPermission(admin, 'posts:write')).toBe(true)
expect(rbac.hasPermission(admin, 'posts:delete')).toBe(true)
})
it('should not allow non-post actions', () => {
rbac.createRole('admin', ['posts:*'])
const admin = { id: '1', roles: ['admin'] }
expect(rbac.hasPermission(admin, 'users:delete')).toBe(false)
})
})Next Steps
- Learn about Role Hierarchy
- Understand Permissions
- Explore API Reference
