RBAC & Permissions
Role-Based Access Control system in HiveForge.
Overview
HiveForge uses a flexible RBAC system with:
- Predefined roles (Owner, Admin, Member, Viewer)
- Granular permissions
- Resource-level access control
- Easy customization
Default Roles
Owner
Full control of organization.
Permissions:
- All organization management
- Billing and subscription
- Member management
- Delete organization
Limitations:
- Only one owner per organization
- Cannot be removed (must transfer ownership)
Admin
Administrative access without billing.
Permissions:
- Manage members (except owner)
- Update organization settings
- Manage API keys
- Access all resources
Cannot:
- Manage billing
- Delete organization
- Change owner
Member
Standard user access.
Permissions:
- View organization resources
- Create and edit own resources
- Collaborate with team
Cannot:
- Manage members
- Change settings
- Access billing
Viewer
Read-only access.
Permissions:
- View organization
- View resources
Cannot:
- Edit anything
- Create resources
Permission System
Permission Format
Permissions use format: resource.action
Examples:
organizations.updatemembers.invitebilling.manageapi_keys.create
Check Permissions
Frontend
import { usePermissions } from '@/hooks/usePermissions'
function EditButton() {
const { hasPermission } = usePermissions()
if (!hasPermission('organizations.update')) {
return null
}
return <button>Edit Organization</button>
}Backend
from app.services.permissions import check_permission
@router.put("/organizations/{org_id}")
async def update_organization(
org_id: str,
user = Depends(get_current_user)
):
if not await check_permission(user.id, org_id, "organizations.update"):
raise HTTPException(status_code=403, detail="Permission denied")
# Update organizationCustom Permissions
Add custom permissions:
INSERT INTO permissions (name, resource, action, description)
VALUES (
'projects.archive',
'projects',
'archive',
'Archive projects'
);
-- Assign to role
INSERT INTO role_permissions (role_id, permission_id)
SELECT
(SELECT id FROM roles WHERE name = 'admin'),
(SELECT id FROM permissions WHERE name = 'projects.archive');Role Management
Assign Roles
await updateMemberRole({
organizationId: org.id,
userId: user.id,
role: 'admin',
})Custom Roles
Create custom roles:
const role = await createRole({
name: 'Project Manager',
permissions: [
'projects.create',
'projects.update',
'projects.delete',
'members.view',
],
})Permission Scopes
Organization-Level
Permissions apply to entire organization:
hasPermission('organizations.update')Resource-Level
Permissions for specific resources:
hasPermission('projects.delete', projectId)User Permissions
Individual user overrides:
-- Grant specific permission to user
INSERT INTO user_permissions (user_id, organization_id, permission_id, granted)
VALUES (user_id, org_id, permission_id, true);
-- Revoke permission
UPDATE user_permissions
SET granted = false
WHERE user_id = ? AND permission_id = ?;Best Practices
1. Principle of Least Privilege
Give users minimum permissions needed:
// Good: Member role for standard users
await inviteMember(email, 'member')
// Bad: Admin role when not needed
await inviteMember(email, 'admin')2. Check Permissions Early
Verify permissions before operations:
async function deleteProject(projectId: string) {
// Check permission first
if (!hasPermission('projects.delete')) {
throw new PermissionError()
}
// Then perform operation
await api.delete(`/projects/${projectId}`)
}3. Audit Permission Changes
Log permission changes:
await audit_log(
user_id=admin_id,
action="permission.granted",
resource_type="user",
resource_id=user_id,
metadata={
"permission": "organizations.update",
"granted_by": admin_id
}
)Examples
Protected Component
'use client'
import { usePermissions } from '@/hooks/usePermissions'
import { Button } from '@/components/ui/Button'
export function OrganizationSettings() {
const { hasPermission } = usePermissions()
const canUpdate = hasPermission('organizations.update')
const canDelete = hasPermission('organizations.delete')
const canManageBilling = hasPermission('billing.manage')
return (
<div>
<h1>Settings</h1>
{canUpdate && (
<section>
<h2>General Settings</h2>
<EditForm />
</section>
)}
{canManageBilling && (
<section>
<h2>Billing</h2>
<BillingSettings />
</section>
)}
{canDelete && (
<section>
<h2>Danger Zone</h2>
<Button variant="danger" onClick={handleDelete}>
Delete Organization
</Button>
</section>
)}
</div>
)
}Protected API Route
from app.core.permissions import require_permission
@router.delete("/organizations/{org_id}")
@require_permission("organizations.delete")
async def delete_organization(
org_id: str,
user = Depends(get_current_user)
):
"""Delete organization (requires organizations.delete permission)."""
await OrganizationService.delete(org_id, user.id)
return {"status": "deleted"}Permission Decorator
from functools import wraps
from fastapi import HTTPException
def require_permission(permission: str):
"""Decorator to require specific permission."""
def decorator(func):
@wraps(func)
async def wrapper(*args, **kwargs):
# Get user from dependency
user = kwargs.get('user')
org_id = kwargs.get('org_id')
# Check permission
has_perm = await check_permission(
user.id, org_id, permission
)
if not has_perm:
raise HTTPException(
status_code=403,
detail=f"Missing permission: {permission}"
)
return await func(*args, **kwargs)
return wrapper
return decorator