161 lines
5.2 KiB
Python
161 lines
5.2 KiB
Python
"""
|
|
RBAC routes for the backend API.
|
|
Implements endpoints for role-based access control permissions.
|
|
"""
|
|
|
|
from fastapi import APIRouter, HTTPException, Depends, Query, Request
|
|
from typing import Optional
|
|
import logging
|
|
|
|
from modules.security.auth import getCurrentUser, limiter
|
|
from modules.datamodels.datamodelUam import User, UserPermissions, AccessLevel
|
|
from modules.datamodels.datamodelRbac import AccessRuleContext
|
|
from modules.interfaces.interfaceDbAppObjects import getInterface
|
|
|
|
# Configure logger
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(
|
|
prefix="/api/rbac",
|
|
tags=["RBAC"],
|
|
responses={404: {"description": "Not found"}}
|
|
)
|
|
|
|
|
|
@router.get("/permissions", response_model=UserPermissions)
|
|
@limiter.limit("60/minute")
|
|
async def getPermissions(
|
|
request: Request,
|
|
context: str = Query(..., description="Context type: DATA, UI, or RESOURCE"),
|
|
item: Optional[str] = Query(None, description="Item identifier (table name, UI path, or resource path)"),
|
|
currentUser: User = Depends(getCurrentUser)
|
|
) -> UserPermissions:
|
|
"""
|
|
Get RBAC permissions for the current user for a specific context and item.
|
|
|
|
Query Parameters:
|
|
- context: Context type (DATA, UI, or RESOURCE)
|
|
- item: Optional item identifier. For DATA: table name (e.g., "UserInDB"),
|
|
For UI: cascading string (e.g., "playground.voice.settings"),
|
|
For RESOURCE: cascading string (e.g., "ai.model.anthropic")
|
|
|
|
Returns:
|
|
- UserPermissions object with view, read, create, update, delete permissions
|
|
|
|
Examples:
|
|
- GET /api/rbac/permissions?context=DATA&item=UserInDB
|
|
- GET /api/rbac/permissions?context=UI&item=playground.voice.settings
|
|
- GET /api/rbac/permissions?context=RESOURCE&item=ai.model.anthropic
|
|
"""
|
|
try:
|
|
# Validate context
|
|
try:
|
|
accessContext = AccessRuleContext(context.upper())
|
|
except ValueError:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Invalid context '{context}'. Must be one of: DATA, UI, RESOURCE"
|
|
)
|
|
|
|
# Get interface and RBAC permissions
|
|
interface = getInterface(currentUser)
|
|
if not interface.rbac:
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail="RBAC interface not available"
|
|
)
|
|
|
|
# Get permissions
|
|
permissions = interface.rbac.getUserPermissions(
|
|
currentUser,
|
|
accessContext,
|
|
item or ""
|
|
)
|
|
|
|
return permissions
|
|
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Error getting RBAC permissions: {str(e)}")
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail=f"Failed to get permissions: {str(e)}"
|
|
)
|
|
|
|
|
|
@router.get("/rules", response_model=list)
|
|
@limiter.limit("30/minute")
|
|
async def getAccessRules(
|
|
request: Request,
|
|
roleLabel: Optional[str] = Query(None, description="Filter by role label"),
|
|
context: Optional[str] = Query(None, description="Filter by context (DATA, UI, RESOURCE)"),
|
|
item: Optional[str] = Query(None, description="Filter by item identifier"),
|
|
currentUser: User = Depends(getCurrentUser)
|
|
) -> list:
|
|
"""
|
|
Get access rules with optional filters.
|
|
Only returns rules that the current user has permission to view.
|
|
|
|
Query Parameters:
|
|
- roleLabel: Optional role label filter
|
|
- context: Optional context filter (DATA, UI, RESOURCE)
|
|
- item: Optional item filter
|
|
|
|
Returns:
|
|
- List of AccessRule objects
|
|
"""
|
|
try:
|
|
# Get interface
|
|
interface = getInterface(currentUser)
|
|
|
|
# Check if user has permission to view access rules
|
|
# For now, only sysadmin can view rules
|
|
if not interface.rbac:
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail="RBAC interface not available"
|
|
)
|
|
|
|
# Check permission - only sysadmin can view rules
|
|
permissions = interface.rbac.getUserPermissions(
|
|
currentUser,
|
|
AccessRuleContext.DATA,
|
|
"AccessRule"
|
|
)
|
|
|
|
if not permissions.view or permissions.read == AccessLevel.NONE:
|
|
raise HTTPException(
|
|
status_code=403,
|
|
detail="No permission to view access rules"
|
|
)
|
|
|
|
# Parse context if provided
|
|
accessContext = None
|
|
if context:
|
|
try:
|
|
accessContext = AccessRuleContext(context.upper())
|
|
except ValueError:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Invalid context '{context}'. Must be one of: DATA, UI, RESOURCE"
|
|
)
|
|
|
|
# Get rules
|
|
rules = interface.getAccessRules(
|
|
roleLabel=roleLabel,
|
|
context=accessContext,
|
|
item=item
|
|
)
|
|
|
|
# Convert to dict for JSON serialization
|
|
return [rule.model_dump() for rule in rules]
|
|
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
logger.error(f"Error getting access rules: {str(e)}")
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail=f"Failed to get access rules: {str(e)}"
|
|
)
|