-
Notifications
You must be signed in to change notification settings - Fork 1
Role Based Access Control
arminrad edited this page Mar 16, 2026
·
2 revisions
Granular permissions and role management
Multi-tier permission system:
- Roles: Admin, User, Developer, Support
- Permissions: Read, Write, Delete, Admin
- Resources: Users, API Keys, Payments, Models
- Scope-based: Fine-grained access control
- Manage own account
- Create API keys
- Make API requests
- View own usage
- Purchase credits
- All User permissions
- Access API documentation
- View system status
- Test endpoints
- View user accounts
- Read user activity
- Assist with issues
- Cannot modify payments
- Full system access
- User management
- Payment operations
- System configuration
- Audit log access
CREATE TABLE roles (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
description TEXT,
permissions JSONB NOT NULL,
is_system BOOLEAN DEFAULT false,
created_at TIMESTAMP DEFAULT NOW()
);CREATE TABLE user_roles (
user_id INTEGER NOT NULL REFERENCES users(id),
role_id INTEGER NOT NULL REFERENCES roles(id),
assigned_at TIMESTAMP DEFAULT NOW(),
assigned_by INTEGER REFERENCES users(id),
PRIMARY KEY (user_id, role_id)
);{
"users": {
"read": true,
"write": false,
"delete": false
},
"api_keys": {
"read": true,
"write": true,
"delete": true
},
"payments": {
"read": true,
"write": false,
"delete": false
},
"admin": {
"read": false,
"write": false,
"delete": false
}
}from src.services.roles import check_permission
@app.get("/admin/users")
async def list_users(user: User = Depends(get_current_user)):
if not check_permission(user, "users", "read"):
raise HTTPException(403, "Permission denied")
return get_all_users()from src.security.deps import require_permission
@app.delete("/users/{user_id}")
@require_permission("users", "delete")
async def delete_user(user_id: int):
return delete_user_account(user_id)GET /user/roles
Response:
{
"roles": [
{
"id": 1,
"name": "user",
"permissions": {...}
}
]
}POST /admin/users/{user_id}/roles
Request:
{
"role_id": 2
}DELETE /admin/users/{user_id}/roles/{role_id}
POST /admin/roles
Request:
{
"name": "content_moderator",
"description": "Can moderate user content",
"permissions": {
"users": {"read": true},
"content": {"read": true, "write": true}
}
}# Can read any user
check_permission(user, "users", "read")
# Can write own data only
check_permission(user, "users", "write", resource_id=user.id){
"scope_permissions": {
"read": ["models", "balance"],
"write": ["chat"],
"admin": []
}
}Admin
├── Support
│ └── Developer
│ └── User
Higher roles inherit lower role permissions.
# Create support role
support_role = create_role(
name="support",
permissions={
"users": {"read": true, "write": false},
"tickets": {"read": true, "write": true},
"payments": {"read": true, "write": false}
}
)
# Assign to support team member
assign_role(user_id=support_user.id, role_id=support_role.id)# Limited scope key
api_key = create_api_key(
user_id=user.id,
scope_permissions={
"read": ["models"], # Can only read models
"write": [], # Cannot write anything
"admin": [] # No admin access
}
)def check_permission(
user: User,
resource: str,
action: str,
resource_id: Optional[int] = None
) -> bool:
# Get user roles
roles = get_user_roles(user.id)
# Check each role
for role in roles:
perms = role.permissions.get(resource, {})
# Check permission
if perms.get(action):
# Check resource ownership if needed
if resource_id and not is_owner(user.id, resource_id):
continue
return True
return False@app.middleware("http")
async def rbac_middleware(request: Request, call_next):
# Get user and required permission
user = get_user_from_request(request)
required = get_required_permission(request.url.path, request.method)
# Check permission
if required and not check_permission(user, *required):
return JSONResponse(
status_code=403,
content={"error": "Permission denied"}
)
return await call_next(request)All role changes are logged:
await log_audit_event(
event_type="role_assigned",
event_category="authorization",
user_id=target_user.id,
admin_id=admin.id,
details={
"role_id": role.id,
"role_name": role.name
},
severity="high"
)- Principle of least privilege: Grant minimum needed
- Regular audits: Review role assignments
- Custom roles: Create specific roles as needed
- Temporary access: Use expiration dates
- Audit all changes: Log role modifications
- Test permissions: Verify access controls
- Document roles: Clear role descriptions
# Users cannot grant roles they don't have
def can_assign_role(admin: User, role: Role) -> bool:
admin_roles = get_user_roles(admin.id)
# Check if admin has all permissions in role
for resource, perms in role.permissions.items():
for action, allowed in perms.items():
if allowed and not check_permission(admin, resource, action):
return False
return True# Users can only modify their own resources
def is_owner(user_id: int, resource_id: int) -> bool:
resource = get_resource(resource_id)
return resource.user_id == user_id# Old: Simple admin check
if not user.is_admin:
raise PermissionDenied()
# New: RBAC check
if not check_permission(user, "users", "write"):
raise PermissionDenied()- API Key Management - Key scopes
- Audit System - Role change auditing
- Security Best Practices
Last Updated: December 2024 Status: Production Ready
- API-Key-Management — Keys are scoped by role
- Audit-System — Role changes are audit-logged
- Activity-Logging — Role-based actions logged
Reading Path (start here, in order)
- Conceptual Model
- Stability Definition
- Conceptual Model Features
- Features
- Delta Report
- Features-Acceptance-Criteria
Testing
Security & Access
Billing
Monitoring
Features
Providers
Operations
Data References