refactor(rbac): migrate API permission checks to resource:action format
Replace all legacy dotted permission codes (vms.view, proxmox.manage, ...) in PermissionChecker/has_permission across app/api with the new resource:action format (vm:view, server:manage, ...), per LEGACY_PERMISSION_MAP. All 33 codes used by the API are now registered in the permission registry. Also switch the /api/auth/me admin branch to get_all_permissions_v2 so the endpoint emits permissions in the new format only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -255,9 +255,9 @@ def get_current_user_info(current_user: User = Depends(get_current_user)):
|
||||
if current_user.role:
|
||||
permissions = current_user.role.permissions or {}
|
||||
elif current_user.is_admin:
|
||||
# Admin has all permissions
|
||||
# Admin has all permissions (new resource:action format)
|
||||
from ..services.security_service import SecurityService
|
||||
all_perms = SecurityService.get_all_permissions()
|
||||
all_perms = SecurityService.get_all_permissions_v2()
|
||||
for category, perms in all_perms.items():
|
||||
for perm in perms:
|
||||
permissions[perm] = True
|
||||
|
||||
@@ -14,7 +14,7 @@ router = APIRouter()
|
||||
@router.get("/api/dashboard/alerts")
|
||||
def get_dashboard_alerts(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("logs.view"))
|
||||
current_user: User = Depends(PermissionChecker("log:view"))
|
||||
):
|
||||
"""Вернуть события error/critical за последние 24 ч для дашборда"""
|
||||
since_24h = utcnow() - timedelta(hours=24)
|
||||
|
||||
@@ -33,7 +33,7 @@ def get_networks(
|
||||
is_active: Optional[bool] = None,
|
||||
proxmox_server_id: Optional[int] = None,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.view"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:view"))
|
||||
):
|
||||
"""Get all IPAM networks"""
|
||||
query = db.query(IPAMNetwork)
|
||||
@@ -75,7 +75,7 @@ def get_networks(
|
||||
def get_network(
|
||||
network_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.view"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:view"))
|
||||
):
|
||||
"""Get a specific IPAM network"""
|
||||
network = db.query(IPAMNetwork).filter(IPAMNetwork.id == network_id).first()
|
||||
@@ -98,7 +98,7 @@ def get_network(
|
||||
def get_network_stats(
|
||||
network_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.view"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:view"))
|
||||
):
|
||||
"""Get detailed statistics for a network"""
|
||||
ipam = IPAMService(db)
|
||||
@@ -114,7 +114,7 @@ def get_network_stats(
|
||||
def get_network_ip_map(
|
||||
network_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.view"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:view"))
|
||||
):
|
||||
"""Get IP address map for visualization"""
|
||||
ipam = IPAMService(db)
|
||||
@@ -132,7 +132,7 @@ def get_network_ip_map(
|
||||
def create_network(
|
||||
network_data: IPAMNetworkCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""Create a new IPAM network"""
|
||||
# Check for duplicate network CIDR
|
||||
@@ -154,7 +154,7 @@ def update_network(
|
||||
network_id: int,
|
||||
network_data: IPAMNetworkUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""Update an IPAM network"""
|
||||
network = db.query(IPAMNetwork).filter(IPAMNetwork.id == network_id).first()
|
||||
@@ -177,7 +177,7 @@ def delete_network(
|
||||
network_id: int,
|
||||
force: bool = False,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""Delete an IPAM network"""
|
||||
network = db.query(IPAMNetwork).filter(IPAMNetwork.id == network_id).first()
|
||||
@@ -210,7 +210,7 @@ def get_pools(
|
||||
network_id: Optional[int] = None,
|
||||
is_active: Optional[bool] = None,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.view"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:view"))
|
||||
):
|
||||
"""Get all IP pools"""
|
||||
query = db.query(IPAMPool)
|
||||
@@ -243,7 +243,7 @@ def get_pools(
|
||||
def create_pool(
|
||||
pool_data: IPAMPoolCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""Create a new IP pool"""
|
||||
# Validate network exists
|
||||
@@ -275,7 +275,7 @@ def update_pool(
|
||||
pool_id: int,
|
||||
pool_data: IPAMPoolUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""Update an IP pool"""
|
||||
pool = db.query(IPAMPool).filter(IPAMPool.id == pool_id).first()
|
||||
@@ -309,7 +309,7 @@ def update_pool(
|
||||
def delete_pool(
|
||||
pool_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""Delete an IP pool"""
|
||||
pool = db.query(IPAMPool).filter(IPAMPool.id == pool_id).first()
|
||||
@@ -337,7 +337,7 @@ def get_allocations(
|
||||
limit: int = Query(default=100, le=1000),
|
||||
offset: int = 0,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.view"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:view"))
|
||||
):
|
||||
"""Get IP allocations with filtering"""
|
||||
query = db.query(IPAMAllocation)
|
||||
@@ -400,7 +400,7 @@ def get_allocations(
|
||||
def get_allocation(
|
||||
allocation_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.view"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:view"))
|
||||
):
|
||||
"""Get a specific allocation"""
|
||||
allocation = db.query(IPAMAllocation).filter(IPAMAllocation.id == allocation_id).first()
|
||||
@@ -421,7 +421,7 @@ def get_allocation(
|
||||
def create_allocation(
|
||||
alloc_data: IPAMAllocationCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""Manually create an IP allocation"""
|
||||
ipam = IPAMService(db)
|
||||
@@ -454,7 +454,7 @@ def create_allocation(
|
||||
def auto_allocate_ip(
|
||||
request: IPAMAutoAllocateRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""Automatically allocate the next available IP"""
|
||||
ipam = IPAMService(db)
|
||||
@@ -480,7 +480,7 @@ def update_allocation(
|
||||
allocation_id: int,
|
||||
alloc_data: IPAMAllocationUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""Update an allocation"""
|
||||
allocation = db.query(IPAMAllocation).filter(IPAMAllocation.id == allocation_id).first()
|
||||
@@ -505,7 +505,7 @@ def delete_allocation(
|
||||
allocation_id: int,
|
||||
reason: Optional[str] = None,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""Release/delete an IP allocation"""
|
||||
allocation = db.query(IPAMAllocation).filter(IPAMAllocation.id == allocation_id).first()
|
||||
@@ -530,7 +530,7 @@ def get_next_available_ip(
|
||||
network_id: int,
|
||||
pool_id: Optional[int] = None,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.view"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:view"))
|
||||
):
|
||||
"""Get the next available IP in a network (without allocating)"""
|
||||
ipam = IPAMService(db)
|
||||
@@ -551,7 +551,7 @@ def get_history(
|
||||
action: Optional[str] = None,
|
||||
limit: int = Query(default=100, le=500),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.view"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:view"))
|
||||
):
|
||||
"""Get IPAM history"""
|
||||
query = db.query(IPAMHistory)
|
||||
@@ -573,7 +573,7 @@ def get_ip_history(
|
||||
ip_address: str,
|
||||
limit: int = Query(default=50, le=200),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.view"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:view"))
|
||||
):
|
||||
"""Get history for a specific IP address"""
|
||||
ipam = IPAMService(db)
|
||||
@@ -586,7 +586,7 @@ def get_ip_history(
|
||||
def detect_conflicts(
|
||||
network_id: Optional[int] = None,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.view"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:view"))
|
||||
):
|
||||
"""Detect IP address conflicts"""
|
||||
ipam = IPAMService(db)
|
||||
@@ -597,7 +597,7 @@ def detect_conflicts(
|
||||
@router.get("/api/summary")
|
||||
def get_ipam_summary(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.view"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:view"))
|
||||
):
|
||||
"""Get overall IPAM summary"""
|
||||
networks_count = db.query(IPAMNetwork).filter(IPAMNetwork.is_active == True).count()
|
||||
@@ -640,7 +640,7 @@ def get_ipam_summary(
|
||||
@router.get("/api/orphans")
|
||||
def get_orphan_allocations(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""
|
||||
Get orphan IPAM allocations - IPs allocated to VMs that no longer exist.
|
||||
@@ -704,7 +704,7 @@ def get_orphan_allocations(
|
||||
@router.post("/api/cleanup-orphans")
|
||||
async def cleanup_orphan_allocations(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""
|
||||
Clean up orphan IPAM allocations - release IPs for VMs that no longer exist.
|
||||
@@ -762,7 +762,7 @@ def _extract_primary_ipv4(interfaces: list, parsed_networks: list):
|
||||
@router.get("/api/unlinked")
|
||||
def get_unlinked_allocations(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""
|
||||
Return VM instances that have no IPAM allocation linked to them (by vmid+server_id).
|
||||
@@ -813,7 +813,7 @@ class LinkAllocationsRequest(BaseModel):
|
||||
def link_allocations_to_vms(
|
||||
body: LinkAllocationsRequest = None,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("ipam.manage"))
|
||||
current_user: User = Depends(PermissionChecker("ipam:manage"))
|
||||
):
|
||||
"""
|
||||
Scan Proxmox servers, get VM IP addresses via QEMU guest agent / LXC config,
|
||||
|
||||
@@ -91,7 +91,7 @@ async def get_logs(
|
||||
page: int = Query(1, ge=1, description="Page number"),
|
||||
limit: int = Query(50, ge=1, le=500, description="Items per page"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("logs.view"))
|
||||
current_user: User = Depends(PermissionChecker("log:view"))
|
||||
):
|
||||
"""Get logs with filtering and pagination"""
|
||||
|
||||
@@ -146,7 +146,7 @@ async def get_logs(
|
||||
async def get_log_stats(
|
||||
hours: int = Query(24, ge=1, le=720, description="Period in hours"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("logs.view"))
|
||||
current_user: User = Depends(PermissionChecker("log:view"))
|
||||
):
|
||||
"""Get log statistics for the specified period"""
|
||||
stats = LoggingService.get_stats(db, hours=hours)
|
||||
@@ -157,7 +157,7 @@ async def get_log_stats(
|
||||
async def cleanup_logs(
|
||||
days: int = Query(30, ge=1, le=365, description="Delete logs older than this many days"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("logs.delete"))
|
||||
current_user: User = Depends(PermissionChecker("log:delete"))
|
||||
):
|
||||
"""Delete old logs (admin only)"""
|
||||
if not current_user.is_admin:
|
||||
|
||||
@@ -27,7 +27,7 @@ async def get_notifications(
|
||||
level: Optional[str] = Query(None, description="Filter by level: critical, warning, info, success"),
|
||||
limit: int = Query(50, ge=1, le=100),
|
||||
offset: int = Query(0, ge=0),
|
||||
current_user: User = Depends(PermissionChecker("notifications.manage")),
|
||||
current_user: User = Depends(PermissionChecker("notification:manage")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
@@ -58,7 +58,7 @@ async def get_notifications(
|
||||
|
||||
@router.get("/unread-count")
|
||||
async def get_unread_count(
|
||||
current_user: User = Depends(PermissionChecker("notifications.manage")),
|
||||
current_user: User = Depends(PermissionChecker("notification:manage")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get count of unread notifications"""
|
||||
@@ -69,7 +69,7 @@ async def get_unread_count(
|
||||
@router.patch("/{notification_id}/read", response_model=NotificationResponse)
|
||||
async def mark_notification_as_read(
|
||||
notification_id: int,
|
||||
current_user: User = Depends(PermissionChecker("notifications.manage")),
|
||||
current_user: User = Depends(PermissionChecker("notification:manage")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Mark notification as read"""
|
||||
@@ -87,7 +87,7 @@ async def mark_notification_as_read(
|
||||
|
||||
@router.post("/mark-all-read")
|
||||
async def mark_all_notifications_as_read(
|
||||
current_user: User = Depends(PermissionChecker("notifications.manage")),
|
||||
current_user: User = Depends(PermissionChecker("notification:manage")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Mark all notifications as read"""
|
||||
@@ -98,7 +98,7 @@ async def mark_all_notifications_as_read(
|
||||
@router.delete("/{notification_id}")
|
||||
async def delete_notification(
|
||||
notification_id: int,
|
||||
current_user: User = Depends(PermissionChecker("notifications.manage")),
|
||||
current_user: User = Depends(PermissionChecker("notification:manage")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Delete a notification"""
|
||||
@@ -116,7 +116,7 @@ async def delete_notification(
|
||||
|
||||
@router.delete("/read/all")
|
||||
async def delete_all_read_notifications(
|
||||
current_user: User = Depends(PermissionChecker("notifications.manage")),
|
||||
current_user: User = Depends(PermissionChecker("notification:manage")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Delete all read notifications"""
|
||||
@@ -128,7 +128,7 @@ async def delete_all_read_notifications(
|
||||
|
||||
@router.get("/preferences", response_model=NotificationPreferenceResponse)
|
||||
async def get_notification_preferences(
|
||||
current_user: User = Depends(PermissionChecker("notifications.manage")),
|
||||
current_user: User = Depends(PermissionChecker("notification:manage")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get user notification preferences"""
|
||||
@@ -139,7 +139,7 @@ async def get_notification_preferences(
|
||||
@router.put("/preferences", response_model=NotificationPreferenceResponse)
|
||||
async def update_notification_preferences(
|
||||
prefs_data: NotificationPreferenceUpdate,
|
||||
current_user: User = Depends(PermissionChecker("notifications.manage")),
|
||||
current_user: User = Depends(PermissionChecker("notification:manage")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Update user notification preferences"""
|
||||
@@ -157,7 +157,7 @@ async def update_notification_preferences(
|
||||
|
||||
@router.get("/channels/status")
|
||||
async def get_channels_status(
|
||||
current_user: User = Depends(PermissionChecker("notifications.manage")),
|
||||
current_user: User = Depends(PermissionChecker("notification:manage")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
@@ -175,7 +175,7 @@ async def get_channels_status(
|
||||
@router.post("/test")
|
||||
async def send_test_notification(
|
||||
channel: str = Query("all", description="Channel to test: all, email, telegram, inapp"),
|
||||
current_user: User = Depends(PermissionChecker("notifications.manage")),
|
||||
current_user: User = Depends(PermissionChecker("notification:manage")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
@@ -193,7 +193,7 @@ async def send_test_notification(
|
||||
@router.post("/telegram/verify")
|
||||
async def verify_telegram_chat(
|
||||
chat_id: str = Query(..., description="Telegram chat ID to verify"),
|
||||
current_user: User = Depends(PermissionChecker("notifications.manage")),
|
||||
current_user: User = Depends(PermissionChecker("notification:manage")),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
@@ -237,7 +237,7 @@ async def verify_telegram_chat(
|
||||
|
||||
@router.get("/telegram/bot-info")
|
||||
async def get_telegram_bot_info(
|
||||
current_user: User = Depends(PermissionChecker("notifications.manage"))
|
||||
current_user: User = Depends(PermissionChecker("notification:manage"))
|
||||
):
|
||||
"""Get information about the configured Telegram bot"""
|
||||
from ..services.notification_channels import get_telegram_channel
|
||||
|
||||
@@ -37,7 +37,7 @@ def check_vm_access(db: Session, current_user: User, server_id: int, vmid: int)
|
||||
if current_user.role and current_user.role.permissions:
|
||||
perms = current_user.role.permissions
|
||||
# If user has full vms:view, they can access all VMs
|
||||
has_full_view = perms.get('vms.view', False) or perms.get('vms:view', False)
|
||||
has_full_view = perms.get('vm:view', False) or perms.get('vms:view', False)
|
||||
has_own_only = perms.get('vms:view:own', False) or perms.get('vms.view.own', False)
|
||||
|
||||
if has_full_view and not has_own_only:
|
||||
|
||||
@@ -59,7 +59,7 @@ def _get_password_client(server: ProxmoxServer, override_password: str = None) -
|
||||
@router.get("/api/cluster/topology")
|
||||
def get_cluster_topology(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view")),
|
||||
current_user: User = Depends(PermissionChecker("server:view")),
|
||||
):
|
||||
"""
|
||||
Получить все серверы, сгруппированные по кластеру.
|
||||
@@ -94,7 +94,7 @@ def get_cluster_topology(
|
||||
async def create_cluster(
|
||||
body: dict,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.cluster.manage")),
|
||||
current_user: User = Depends(PermissionChecker("cluster:manage")),
|
||||
):
|
||||
"""
|
||||
Создать новый Proxmox corosync кластер на указанной ноде.
|
||||
@@ -175,7 +175,7 @@ async def create_cluster(
|
||||
def get_cluster_join_info(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.cluster.manage")),
|
||||
current_user: User = Depends(PermissionChecker("cluster:manage")),
|
||||
):
|
||||
"""
|
||||
Получить fingerprint и join-данные с кластерной ноды.
|
||||
@@ -213,7 +213,7 @@ def get_cluster_join_info(
|
||||
def pre_join_check(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.cluster.manage")),
|
||||
current_user: User = Depends(PermissionChecker("cluster:manage")),
|
||||
):
|
||||
"""
|
||||
Проверить ноду перед присоединением к кластеру:
|
||||
@@ -287,7 +287,7 @@ def pre_join_check(
|
||||
async def prepare_join(
|
||||
body: dict,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.cluster.manage")),
|
||||
current_user: User = Depends(PermissionChecker("cluster:manage")),
|
||||
):
|
||||
"""
|
||||
Создать резервные копии всех VM/LXC на ноде через vzdump и удалить их,
|
||||
@@ -446,7 +446,7 @@ async def prepare_join(
|
||||
async def join_cluster(
|
||||
body: dict,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.cluster.manage")),
|
||||
current_user: User = Depends(PermissionChecker("cluster:manage")),
|
||||
):
|
||||
"""
|
||||
Присоединить standalone-ноду к существующему кластеру.
|
||||
@@ -580,7 +580,7 @@ def eject_cluster_node(
|
||||
server_id: int,
|
||||
body: dict,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.cluster.manage")),
|
||||
current_user: User = Depends(PermissionChecker("cluster:manage")),
|
||||
):
|
||||
"""
|
||||
Удалить ноду из кластера Proxmox.
|
||||
|
||||
@@ -356,7 +356,7 @@ def _do_lxc_deploy(task_id: int, req: LXCDeployRequest, user_id: int, username:
|
||||
def list_lxc_templates(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view")),
|
||||
current_user: User = Depends(PermissionChecker("vm:view")),
|
||||
):
|
||||
"""
|
||||
Список LXC CT-шаблонов (vztmpl) на хранилищах выбранного Proxmox сервера.
|
||||
@@ -382,7 +382,7 @@ def list_lxc_storages(
|
||||
server_id: int,
|
||||
node: Optional[str] = None,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view")),
|
||||
current_user: User = Depends(PermissionChecker("vm:view")),
|
||||
):
|
||||
"""
|
||||
Список хранилищ ноды, пригодных для rootfs LXC контейнера.
|
||||
@@ -422,7 +422,7 @@ def list_lxc_storages(
|
||||
async def deploy_lxc_from_template(
|
||||
req: LXCDeployRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.create")),
|
||||
current_user: User = Depends(PermissionChecker("vm:create")),
|
||||
):
|
||||
"""
|
||||
Асинхронное создание LXC контейнера из CT-шаблона.
|
||||
@@ -438,7 +438,7 @@ async def deploy_lxc_from_template(
|
||||
raise HTTPException(status_code=400, detail="Укажите имя контейнера")
|
||||
|
||||
# Resolve owner / SSH keys
|
||||
is_admin = current_user.has_permission("users.manage") or current_user.is_admin
|
||||
is_admin = current_user.has_permission("user:manage") or current_user.is_admin
|
||||
instance_owner_id = current_user.id
|
||||
if req.owner_id and req.owner_id != current_user.id:
|
||||
if not is_admin:
|
||||
|
||||
@@ -18,7 +18,7 @@ router = APIRouter()
|
||||
def get_server_nodes(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view"))
|
||||
current_user: User = Depends(PermissionChecker("server:view"))
|
||||
):
|
||||
"""List nodes for a Proxmox server"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -41,7 +41,7 @@ def list_node_networks(
|
||||
server_id: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view"))
|
||||
current_user: User = Depends(PermissionChecker("server:view"))
|
||||
):
|
||||
"""List all network interfaces on a node, with IPAM linkage info."""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -84,7 +84,7 @@ async def create_node_network(
|
||||
node: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Create a new network interface on a node."""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -124,7 +124,7 @@ async def update_node_network(
|
||||
iface: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Update a network interface on a node."""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -155,7 +155,7 @@ def delete_node_network(
|
||||
node: str,
|
||||
iface: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Delete a network interface from a node."""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -184,7 +184,7 @@ def apply_node_network(
|
||||
server_id: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Apply pending network configuration changes on a node."""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -213,7 +213,7 @@ def revert_node_network(
|
||||
server_id: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Revert pending network configuration changes on a node."""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
|
||||
@@ -20,7 +20,7 @@ router = APIRouter()
|
||||
def get_sdn_status(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view"))
|
||||
current_user: User = Depends(PermissionChecker("server:view"))
|
||||
):
|
||||
"""Check if SDN is available on the server"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -52,7 +52,7 @@ def get_sdn_status(
|
||||
def get_sdn_zones(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view"))
|
||||
current_user: User = Depends(PermissionChecker("server:view"))
|
||||
):
|
||||
"""Get all SDN zones"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -74,7 +74,7 @@ async def create_sdn_zone(
|
||||
server_id: int,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Create a new SDN zone"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -112,7 +112,7 @@ def delete_sdn_zone(
|
||||
server_id: int,
|
||||
zone: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Delete an SDN zone"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -140,7 +140,7 @@ def delete_sdn_zone(
|
||||
def get_sdn_vnets(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view"))
|
||||
current_user: User = Depends(PermissionChecker("server:view"))
|
||||
):
|
||||
"""Get all SDN VNets"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -162,7 +162,7 @@ async def create_sdn_vnet(
|
||||
server_id: int,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Create a new SDN VNet"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -201,7 +201,7 @@ def delete_sdn_vnet(
|
||||
server_id: int,
|
||||
vnet: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Delete an SDN VNet"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -230,7 +230,7 @@ def get_sdn_subnets(
|
||||
server_id: int,
|
||||
vnet: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view"))
|
||||
current_user: User = Depends(PermissionChecker("server:view"))
|
||||
):
|
||||
"""Get subnets for a VNet"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -253,7 +253,7 @@ async def create_sdn_subnet(
|
||||
vnet: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Create a subnet in a VNet"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -317,7 +317,7 @@ async def create_sdn_subnet(
|
||||
def apply_sdn_changes(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Apply pending SDN changes"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -347,7 +347,7 @@ async def update_sdn_zone(
|
||||
zone: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Update an existing SDN zone"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -376,7 +376,7 @@ async def update_sdn_vnet(
|
||||
vnet: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Update an existing SDN VNet"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -406,7 +406,7 @@ def delete_sdn_subnet(
|
||||
subnet_cidr: str,
|
||||
delete_ipam_network: bool = False,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Delete a subnet from a VNet. subnet_cidr uses '-' instead of '/' (e.g. 10.0.0.0-24)."""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
|
||||
@@ -29,7 +29,7 @@ router = APIRouter()
|
||||
def list_proxmox_servers(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view"))
|
||||
current_user: User = Depends(PermissionChecker("server:view"))
|
||||
):
|
||||
"""Получить список всех Proxmox серверов (фильтруется по активному workspace)"""
|
||||
from ...api.workspaces import get_workspace_server_ids
|
||||
@@ -83,7 +83,7 @@ def list_proxmox_servers(
|
||||
def create_proxmox_server(
|
||||
server_data: ProxmoxServerCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.servers.add"))
|
||||
current_user: User = Depends(PermissionChecker("server:create"))
|
||||
):
|
||||
"""Добавить новый Proxmox сервер"""
|
||||
# Разрешаем несколько серверов с одинаковым IP (например, разные порты или кластеры)
|
||||
@@ -152,7 +152,7 @@ def create_proxmox_server(
|
||||
def get_proxmox_server(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view"))
|
||||
current_user: User = Depends(PermissionChecker("server:view"))
|
||||
):
|
||||
"""Получить информацию о конкретном Proxmox сервере"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -165,7 +165,7 @@ def get_proxmox_server(
|
||||
def get_server_cluster_info(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view"))
|
||||
current_user: User = Depends(PermissionChecker("server:view"))
|
||||
):
|
||||
"""
|
||||
Получить информацию о кластерном режиме сервера.
|
||||
@@ -216,7 +216,7 @@ def update_proxmox_server(
|
||||
server_id: int,
|
||||
server_data: ProxmoxServerUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.servers.edit"))
|
||||
current_user: User = Depends(PermissionChecker("server:update"))
|
||||
):
|
||||
"""Обновить Proxmox сервер"""
|
||||
from ...proxmox import clear_server_cache
|
||||
@@ -290,7 +290,7 @@ def update_proxmox_server(
|
||||
def delete_proxmox_server(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.servers.delete"))
|
||||
current_user: User = Depends(PermissionChecker("server:delete"))
|
||||
):
|
||||
"""Удалить Proxmox сервер и связанные OS Templates"""
|
||||
from ...proxmox import clear_server_cache
|
||||
@@ -378,7 +378,7 @@ def delete_proxmox_server(
|
||||
@router.post("/api/servers/test", status_code=status.HTTP_200_OK)
|
||||
def test_proxmox_credentials(
|
||||
payload: ProxmoxServerCreate,
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view"))
|
||||
current_user: User = Depends(PermissionChecker("server:view"))
|
||||
):
|
||||
"""Проверить произвольные учётные данные Proxmox без сохранения в БД."""
|
||||
try:
|
||||
@@ -410,7 +410,7 @@ def test_proxmox_credentials(
|
||||
def test_proxmox_connection(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view"))
|
||||
current_user: User = Depends(PermissionChecker("server:view"))
|
||||
):
|
||||
"""Проверить подключение к Proxmox серверу"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -443,7 +443,7 @@ def test_proxmox_connection(
|
||||
def get_server_resources(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""API для получения всех ресурсов (VM + LXC) с Proxmox сервера"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -514,7 +514,7 @@ def get_server_resources(
|
||||
@router.get("/api/resources/all")
|
||||
def get_all_resources(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""API для получения всех ресурсов со всех Proxmox серверов"""
|
||||
proxmox_servers = db.query(ProxmoxServer).all()
|
||||
@@ -595,7 +595,7 @@ def get_storages(
|
||||
node: str = Query(..., description="Node name"),
|
||||
content_type: str = Query(None, description="Filter by content type (images, rootdir, vztmpl, etc)"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view"))
|
||||
current_user: User = Depends(PermissionChecker("server:view"))
|
||||
):
|
||||
"""Получить список хранилищ на ноде Proxmox"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -633,7 +633,7 @@ def get_node_status(
|
||||
server_id: int,
|
||||
node: str = Query(..., description="Node name"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить текущий статус ноды Proxmox (хоста)"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -662,7 +662,7 @@ def get_node_rrddata(
|
||||
node: str = Query(..., description="Node name"),
|
||||
timeframe: str = Query("hour", regex="^(hour|day|week|month|year)$"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить исторические данные ноды для графиков"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
|
||||
@@ -31,7 +31,7 @@ def get_vm_snapshots(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Get all snapshots for a VM"""
|
||||
# VPS-style user isolation
|
||||
@@ -60,7 +60,7 @@ async def create_vm_snapshot(
|
||||
node: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Create a VM snapshot"""
|
||||
# VPS-style user isolation
|
||||
@@ -114,7 +114,7 @@ def delete_vm_snapshot(
|
||||
snapname: str,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Delete a VM snapshot"""
|
||||
# VPS-style user isolation
|
||||
@@ -161,7 +161,7 @@ def rollback_vm_snapshot(
|
||||
node: str,
|
||||
start: bool = False,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Rollback a VM to a snapshot"""
|
||||
# VPS-style user isolation
|
||||
@@ -208,7 +208,7 @@ def get_container_snapshots(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Get all snapshots for a container"""
|
||||
# VPS-style user isolation
|
||||
@@ -237,7 +237,7 @@ async def create_container_snapshot(
|
||||
node: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Create a container snapshot"""
|
||||
# VPS-style user isolation
|
||||
@@ -290,7 +290,7 @@ def delete_container_snapshot(
|
||||
snapname: str,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Delete a container snapshot"""
|
||||
# VPS-style user isolation
|
||||
@@ -337,7 +337,7 @@ def rollback_container_snapshot(
|
||||
node: str,
|
||||
start: bool = False,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Rollback a container to a snapshot"""
|
||||
# VPS-style user isolation
|
||||
@@ -385,7 +385,7 @@ def get_snapshot_archives(
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
offset: int = Query(0, ge=0),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("logs.view"))
|
||||
current_user: User = Depends(PermissionChecker("log:view"))
|
||||
):
|
||||
"""
|
||||
Get archived snapshots from deleted VMs/containers.
|
||||
@@ -432,7 +432,7 @@ def get_snapshot_archives(
|
||||
def get_snapshot_archive_detail(
|
||||
archive_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("logs.view"))
|
||||
current_user: User = Depends(PermissionChecker("log:view"))
|
||||
):
|
||||
"""Get full details of an archived snapshot including config"""
|
||||
archive = db.query(VMSnapshotArchive).filter(VMSnapshotArchive.id == archive_id).first()
|
||||
|
||||
@@ -85,7 +85,7 @@ def get_task_status(
|
||||
upid: str,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить статус задачи по UPID"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -115,7 +115,7 @@ def get_task_log(
|
||||
start: int = 0,
|
||||
limit: int = 50,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить лог задачи по UPID"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
|
||||
@@ -58,7 +58,7 @@ class VMOwnerUpdate(BaseModel):
|
||||
@router.post("/api/sync-vms")
|
||||
def sync_vms_now(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""
|
||||
Force immediate synchronization of VMs/containers from all Proxmox servers.
|
||||
@@ -89,7 +89,7 @@ def sync_vms_now(
|
||||
def get_all_virtual_machines(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""
|
||||
API для получения всех VM/LXC в плоском списке для таблицы.
|
||||
@@ -145,7 +145,7 @@ def get_all_virtual_machines(
|
||||
query = query.filter(VMInstance.server_id.in_(workspace_server_ids))
|
||||
|
||||
# VPS-style user isolation: users with 'user' role see only their own instances
|
||||
# Check if user has 'vms.view' but not 'vms.view:all' (or is user role)
|
||||
# Check if user has 'vm:view' but not 'vms.view:all' (or is user role)
|
||||
user_role = current_user.role.name if current_user.role else None
|
||||
is_limited_user = user_role == 'user'
|
||||
|
||||
@@ -154,7 +154,7 @@ def get_all_virtual_machines(
|
||||
perms = current_user.role.permissions or {}
|
||||
# If user has vms:view:own but not vms:view (full), filter by owner
|
||||
has_view_own = perms.get('vms:view:own', False) or perms.get('vms.view.own', False)
|
||||
has_view_all = perms.get('vms:view', False) or perms.get('vms.view', False)
|
||||
has_view_all = perms.get('vms:view', False) or perms.get('vm:view', False)
|
||||
|
||||
# Limited users can only see their own VMs
|
||||
if is_limited_user or (has_view_own and not has_view_all):
|
||||
@@ -288,7 +288,7 @@ def execute_vm_command(
|
||||
command: str = Query(..., description="Команда для выполнения"),
|
||||
timeout: int = Query(30, ge=1, le=300, description="Таймаут в секундах"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.console"))
|
||||
current_user: User = Depends(PermissionChecker("vm:console"))
|
||||
):
|
||||
"""
|
||||
Выполнить команду на VM через QEMU guest agent
|
||||
@@ -325,7 +325,7 @@ def execute_vm_script(
|
||||
interpreter: str = Form("/bin/bash", description="Путь к интерпретатору"),
|
||||
timeout: int = Query(60, ge=1, le=600, description="Таймаут в секундах"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.console"))
|
||||
current_user: User = Depends(PermissionChecker("vm:console"))
|
||||
):
|
||||
"""
|
||||
Выполнить bash скрипт на VM через QEMU guest agent
|
||||
@@ -388,7 +388,7 @@ def clone_vm_endpoint(
|
||||
node: str,
|
||||
body: CloneRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.create"))
|
||||
current_user: User = Depends(PermissionChecker("vm:create"))
|
||||
):
|
||||
"""Клонировать существующую VM (qemu) — фоновая задача."""
|
||||
from ...models import DeployTask
|
||||
@@ -419,7 +419,7 @@ def clone_container_endpoint(
|
||||
node: str,
|
||||
body: CloneRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.create"))
|
||||
current_user: User = Depends(PermissionChecker("vm:create"))
|
||||
):
|
||||
"""Клонировать существующий LXC контейнер — фоновая задача."""
|
||||
from ...models import DeployTask
|
||||
@@ -451,7 +451,7 @@ def reinstall_vm_endpoint(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.delete"))
|
||||
current_user: User = Depends(PermissionChecker("vm:delete"))
|
||||
):
|
||||
"""Переустановить VM/LXC из исходного шаблона — фоновая задача."""
|
||||
from ...models import DeployTask, OSTemplate
|
||||
@@ -504,7 +504,7 @@ def change_vm_password_endpoint(
|
||||
node: str,
|
||||
body: ChangePasswordRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.console"))
|
||||
current_user: User = Depends(PermissionChecker("vm:console"))
|
||||
):
|
||||
"""Сменить пароль на VM через QEMU guest agent — фоновая задача."""
|
||||
from ...models import DeployTask
|
||||
@@ -544,7 +544,7 @@ def change_container_password_endpoint(
|
||||
node: str,
|
||||
body: ChangePasswordRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.console"))
|
||||
current_user: User = Depends(PermissionChecker("vm:console"))
|
||||
):
|
||||
"""Сменить пароль в LXC через pct exec/chpasswd — фоновая задача."""
|
||||
from ...models import DeployTask
|
||||
@@ -595,10 +595,10 @@ def control_vm(
|
||||
|
||||
# Проверка прав в зависимости от действия
|
||||
permission_map = {
|
||||
'start': 'vms.start',
|
||||
'stop': 'vms.stop',
|
||||
'shutdown': 'vms.stop',
|
||||
'restart': 'vms.restart',
|
||||
'start': 'vm:start',
|
||||
'stop': 'vm:stop',
|
||||
'shutdown': 'vm:stop',
|
||||
'restart': 'vm:restart',
|
||||
}
|
||||
if not current_user.has_permission(permission_map[action]):
|
||||
raise HTTPException(
|
||||
@@ -738,7 +738,7 @@ async def delete_vm(
|
||||
node: str,
|
||||
force: bool = Query(False, description="Force delete without saving config"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.delete"))
|
||||
current_user: User = Depends(PermissionChecker("vm:delete"))
|
||||
):
|
||||
"""Удалить VM"""
|
||||
from ...i18n import t
|
||||
@@ -940,7 +940,7 @@ async def delete_vm(
|
||||
def get_ha_status(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить статус HA кластера и список ресурсов в HA"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -977,7 +977,7 @@ def get_resource_ha_status(
|
||||
vm_type: str,
|
||||
vmid: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить HA статус конкретной VM или контейнера"""
|
||||
if vm_type not in ['vm', 'ct']:
|
||||
@@ -1031,7 +1031,7 @@ def add_resource_to_ha(
|
||||
state: str = Query("started", description="Target state: started, stopped, enabled, disabled, ignored"),
|
||||
comment: str = Query(None, description="Comment"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Добавить VM или контейнер в HA"""
|
||||
if vm_type not in ['vm', 'ct']:
|
||||
@@ -1094,7 +1094,7 @@ def remove_resource_from_ha(
|
||||
vm_type: str,
|
||||
vmid: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Удалить VM или контейнер из HA"""
|
||||
if vm_type not in ['vm', 'ct']:
|
||||
@@ -1145,7 +1145,7 @@ async def delete_container(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.delete"))
|
||||
current_user: User = Depends(PermissionChecker("vm:delete"))
|
||||
):
|
||||
"""Удалить LXC контейнер"""
|
||||
from ...i18n import t
|
||||
@@ -1269,7 +1269,7 @@ def get_vm_config(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить конфигурацию VM"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -1296,7 +1296,7 @@ async def update_vm_config(
|
||||
node: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Обновить конфигурацию VM (CPU, Memory, etc.)"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -1331,7 +1331,7 @@ async def resize_vm_disk(
|
||||
node: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Изменить размер диска VM"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -1384,7 +1384,7 @@ async def resize_container_disk(
|
||||
node: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Изменить размер диска контейнера"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -1441,7 +1441,7 @@ def get_container_config(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить конфигурацию LXC контейнера"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -1468,7 +1468,7 @@ async def update_container_config(
|
||||
node: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Обновить конфигурацию LXC контейнера"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -1512,10 +1512,10 @@ def control_container(
|
||||
|
||||
# Проверка прав в зависимости от действия
|
||||
permission_map = {
|
||||
'start': 'vms.start',
|
||||
'stop': 'vms.stop',
|
||||
'shutdown': 'vms.stop',
|
||||
'restart': 'vms.restart',
|
||||
'start': 'vm:start',
|
||||
'stop': 'vm:stop',
|
||||
'shutdown': 'vm:stop',
|
||||
'restart': 'vm:restart',
|
||||
}
|
||||
require_permission(current_user, permission_map[action])
|
||||
|
||||
@@ -1594,7 +1594,7 @@ def control_container(
|
||||
def get_server_nodes(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.view"))
|
||||
current_user: User = Depends(PermissionChecker("server:view"))
|
||||
):
|
||||
"""Получить список нод Proxmox сервера вместе с идентификатором кластера"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -1639,7 +1639,7 @@ def get_vm_status(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить детальный статус VM (CPU, RAM, Disk, Network)"""
|
||||
require_vm_access(db, current_user, server_id, vmid)
|
||||
@@ -1666,7 +1666,7 @@ def get_container_status(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить детальный статус LXC контейнера"""
|
||||
require_vm_access(db, current_user, server_id, vmid)
|
||||
@@ -1693,7 +1693,7 @@ def get_vm_interfaces(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить сетевые интерфейсы и IP адреса VM через QEMU guest agent"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -1719,7 +1719,7 @@ def get_container_interfaces(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить сетевые интерфейсы и IP адреса контейнера"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -1745,7 +1745,7 @@ def get_container_status_api(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить статус контейнера"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -1772,7 +1772,7 @@ def get_vm_rrddata(
|
||||
node: str,
|
||||
timeframe: str = Query("hour", regex="^(hour|day|week|month|year)$"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить исторические данные VM для графиков (CPU, RAM, Network, Disk IO)"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -1799,7 +1799,7 @@ def get_container_rrddata(
|
||||
node: str,
|
||||
timeframe: str = Query("hour", regex="^(hour|day|week|month|year)$"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Получить исторические данные контейнера для графиков"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -1827,7 +1827,7 @@ def get_vm_vnc(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.console"))
|
||||
current_user: User = Depends(PermissionChecker("vm:console"))
|
||||
):
|
||||
"""Получить VNC данные для подключения к VM"""
|
||||
import requests
|
||||
@@ -1914,7 +1914,7 @@ def get_container_vnc(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.console"))
|
||||
current_user: User = Depends(PermissionChecker("vm:console"))
|
||||
):
|
||||
"""Получить VNC данные для подключения к LXC контейнеру"""
|
||||
import requests
|
||||
@@ -2017,7 +2017,7 @@ async def vnc_websocket_proxy(
|
||||
if not user:
|
||||
await websocket.close(code=4001, reason="Authentication required")
|
||||
return
|
||||
if not PermissionEngine.has_permission(user, "vms.console"):
|
||||
if not PermissionEngine.has_permission(user, "vm:console"):
|
||||
await websocket.close(code=4003, reason="Permission denied")
|
||||
return
|
||||
if not check_vm_access(db, user, server_id, vmid):
|
||||
@@ -2158,7 +2158,7 @@ def get_saved_vm_config(
|
||||
server_id: int,
|
||||
vmid: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Get saved VM configuration from database"""
|
||||
instance = get_vm_instance(db, server_id, vmid)
|
||||
@@ -2190,7 +2190,7 @@ def get_saved_vm_config(
|
||||
def get_all_lxc_templates(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.view"))
|
||||
current_user: User = Depends(PermissionChecker("template:view"))
|
||||
):
|
||||
"""Получить список LXC шаблонов со всех нод кластера с информацией о shared storage"""
|
||||
logger.info(f"[LXC API] Request for ALL templates from server_id={server_id}")
|
||||
@@ -2219,7 +2219,7 @@ def get_available_lxc_templates(
|
||||
server_id: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.view"))
|
||||
current_user: User = Depends(PermissionChecker("template:view"))
|
||||
):
|
||||
"""Получить список шаблонов доступных для загрузки из репозитория"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -2247,7 +2247,7 @@ async def download_lxc_template(
|
||||
server_id: int,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.manage"))
|
||||
current_user: User = Depends(PermissionChecker("template:manage"))
|
||||
):
|
||||
"""Скачать шаблон LXC из репозитория"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -2318,7 +2318,7 @@ def get_vm_terminal(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.console"))
|
||||
current_user: User = Depends(PermissionChecker("vm:console"))
|
||||
):
|
||||
"""Получить данные для xterm.js терминального подключения к VM"""
|
||||
import requests
|
||||
@@ -2391,7 +2391,7 @@ def get_container_terminal(
|
||||
vmid: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.console"))
|
||||
current_user: User = Depends(PermissionChecker("vm:console"))
|
||||
):
|
||||
"""Получить данные для xterm.js терминального подключения к LXC контейнеру"""
|
||||
import requests
|
||||
@@ -2465,7 +2465,7 @@ async def exec_in_container(
|
||||
node: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.console"))
|
||||
current_user: User = Depends(PermissionChecker("vm:console"))
|
||||
):
|
||||
"""Выполнить команду в LXC контейнере через pct exec"""
|
||||
server = db.query(ProxmoxServer).filter(ProxmoxServer.id == server_id).first()
|
||||
@@ -2520,7 +2520,7 @@ async def terminal_websocket_fixed(
|
||||
if not user:
|
||||
await websocket.close(code=4001, reason="Authentication required")
|
||||
return
|
||||
if not PermissionEngine.has_permission(user, "vms.console"):
|
||||
if not PermissionEngine.has_permission(user, "vm:console"):
|
||||
await websocket.close(code=4003, reason="Permission denied")
|
||||
return
|
||||
if not check_vm_access(db, user, server_id, vmid):
|
||||
@@ -2693,7 +2693,7 @@ async def terminal_websocket_fixed(
|
||||
def create_bulk_operation(
|
||||
request: BulkOperationRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.vm.manage"))
|
||||
current_user: User = Depends(PermissionChecker("vm:manage"))
|
||||
):
|
||||
"""
|
||||
Create a bulk operation task.
|
||||
@@ -2717,7 +2717,7 @@ def create_bulk_operation(
|
||||
|
||||
# For delete action, require higher permission
|
||||
if request.action == 'delete':
|
||||
if not check_permission(current_user, "vms.delete"):
|
||||
if not check_permission(current_user, "vm:delete"):
|
||||
raise HTTPException(status_code=403, detail="Delete permission required")
|
||||
|
||||
# Convert to list of dicts
|
||||
@@ -2842,7 +2842,7 @@ def update_vm_notes(
|
||||
node: str,
|
||||
body: NotesRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Обновить заметку (description) у VM."""
|
||||
require_vm_access(db, current_user, server_id, vmid)
|
||||
@@ -2872,7 +2872,7 @@ def update_container_notes(
|
||||
node: str,
|
||||
body: NotesRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Обновить заметку (description) у LXC."""
|
||||
require_vm_access(db, current_user, server_id, vmid)
|
||||
@@ -2902,7 +2902,7 @@ def list_node_isos(
|
||||
server_id: int,
|
||||
node: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.view"))
|
||||
current_user: User = Depends(PermissionChecker("vm:view"))
|
||||
):
|
||||
"""Список ISO-образов на ноде."""
|
||||
server = _resolve_server(db, server_id)
|
||||
@@ -2917,7 +2917,7 @@ def attach_iso_endpoint(
|
||||
node: str,
|
||||
body: IsoAttachRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Подключить ISO (CD-ROM) к VM."""
|
||||
require_vm_access(db, current_user, server_id, vmid)
|
||||
@@ -2942,7 +2942,7 @@ def detach_iso_endpoint(
|
||||
node: str,
|
||||
body: IsoDetachRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("proxmox.manage"))
|
||||
current_user: User = Depends(PermissionChecker("server:manage"))
|
||||
):
|
||||
"""Отключить ISO (CD-ROM) у VM."""
|
||||
require_vm_access(db, current_user, server_id, vmid)
|
||||
|
||||
@@ -235,7 +235,7 @@ async def change_password(
|
||||
@router.get("/api/panel", response_model=PanelSettingsResponse)
|
||||
async def get_panel_settings(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.view"))
|
||||
current_user: User = Depends(PermissionChecker("setting:view"))
|
||||
):
|
||||
"""Get panel settings"""
|
||||
from ..config import settings
|
||||
@@ -255,7 +255,7 @@ async def get_panel_settings(
|
||||
async def update_panel_settings(
|
||||
data: UpdatePanelSettingsRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.panel"))
|
||||
current_user: User = Depends(PermissionChecker("setting:update"))
|
||||
):
|
||||
"""Update panel settings (requires settings.panel permission)"""
|
||||
updated_fields = []
|
||||
@@ -666,7 +666,7 @@ async def get_version():
|
||||
@router.get("/api/updates/repository")
|
||||
async def get_repository_url(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.manage"))
|
||||
current_user: User = Depends(PermissionChecker("setting:manage"))
|
||||
):
|
||||
"""Get configured git repository URL"""
|
||||
setting = db.query(PanelSettings).filter(PanelSettings.key == "git_repository_url").first()
|
||||
@@ -679,7 +679,7 @@ async def get_repository_url(
|
||||
async def set_repository_url(
|
||||
repository_url: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.manage"))
|
||||
current_user: User = Depends(PermissionChecker("setting:manage"))
|
||||
):
|
||||
"""Set git repository URL for updates"""
|
||||
# Validate URL format
|
||||
@@ -707,7 +707,7 @@ async def set_repository_url(
|
||||
@router.get("/api/updates/check")
|
||||
async def check_updates(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.manage"))
|
||||
current_user: User = Depends(PermissionChecker("setting:manage"))
|
||||
):
|
||||
"""Check for available updates (admin only)"""
|
||||
result = await check_for_updates()
|
||||
@@ -734,7 +734,7 @@ async def updates_status():
|
||||
@router.post("/api/updates/perform")
|
||||
async def perform_system_update(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.manage"))
|
||||
current_user: User = Depends(PermissionChecker("setting:manage"))
|
||||
):
|
||||
"""Perform system update (admin only)"""
|
||||
|
||||
@@ -766,7 +766,7 @@ async def perform_system_update(
|
||||
|
||||
@router.post("/api/updates/reset")
|
||||
async def reset_update(
|
||||
current_user: User = Depends(PermissionChecker("settings.manage"))
|
||||
current_user: User = Depends(PermissionChecker("setting:manage"))
|
||||
):
|
||||
"""Reset update status (admin only)"""
|
||||
reset_update_status()
|
||||
|
||||
@@ -174,7 +174,7 @@ async def download_my_private_key(
|
||||
async def list_user_ssh_keys(
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.manage")),
|
||||
current_user: User = Depends(PermissionChecker("user:manage")),
|
||||
):
|
||||
"""Admin: list a user's SSH keys."""
|
||||
_ensure_target_user(db, user_id)
|
||||
@@ -197,7 +197,7 @@ async def admin_create_user_ssh_key(
|
||||
data: SSHKeyCreate,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.manage")),
|
||||
current_user: User = Depends(PermissionChecker("user:manage")),
|
||||
):
|
||||
target = _ensure_target_user(db, user_id)
|
||||
return _create_key_for_user(db, request, current_user, target.id, data)
|
||||
@@ -213,7 +213,7 @@ async def admin_update_user_ssh_key(
|
||||
data: SSHKeyUpdate,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.manage")),
|
||||
current_user: User = Depends(PermissionChecker("user:manage")),
|
||||
):
|
||||
_ensure_target_user(db, user_id)
|
||||
return _update_key(db, request, current_user, user_id, key_id, data)
|
||||
@@ -225,7 +225,7 @@ async def admin_delete_user_ssh_key(
|
||||
key_id: int,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.manage")),
|
||||
current_user: User = Depends(PermissionChecker("user:manage")),
|
||||
):
|
||||
_ensure_target_user(db, user_id)
|
||||
return _delete_key(db, request, current_user, user_id, key_id)
|
||||
@@ -236,7 +236,7 @@ async def admin_download_user_private_key(
|
||||
user_id: int,
|
||||
key_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.manage")),
|
||||
current_user: User = Depends(PermissionChecker("user:manage")),
|
||||
):
|
||||
_ensure_target_user(db, user_id)
|
||||
return _read_private_key(db, user_id, key_id)
|
||||
@@ -427,7 +427,7 @@ def resolve_ssh_keys_for_deploy(
|
||||
return ""
|
||||
|
||||
allowed_owners = {requester.id}
|
||||
is_admin = requester.has_permission("users.manage") or requester.is_admin
|
||||
is_admin = requester.has_permission("user:manage") or requester.is_admin
|
||||
if is_admin and instance_owner_id:
|
||||
allowed_owners.add(instance_owner_id)
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class DeployTaskStatusResponse(BaseModel):
|
||||
@router.get("/api/groups", response_model=List[OSTemplateGroupResponse])
|
||||
def list_template_groups(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.view"))
|
||||
current_user: User = Depends(PermissionChecker("template:view"))
|
||||
):
|
||||
"""Получить список всех групп шаблонов"""
|
||||
groups = db.query(OSTemplateGroup).order_by(OSTemplateGroup.sort_order).all()
|
||||
@@ -77,7 +77,7 @@ def list_template_groups(
|
||||
def create_template_group(
|
||||
group_data: OSTemplateGroupCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.manage"))
|
||||
current_user: User = Depends(PermissionChecker("template:manage"))
|
||||
):
|
||||
"""Создать новую группу шаблонов"""
|
||||
# Check if group with same name exists
|
||||
@@ -101,7 +101,7 @@ def create_template_group(
|
||||
def get_template_group(
|
||||
group_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.view"))
|
||||
current_user: User = Depends(PermissionChecker("template:view"))
|
||||
):
|
||||
"""Получить группу шаблонов по ID"""
|
||||
group = db.query(OSTemplateGroup).filter(OSTemplateGroup.id == group_id).first()
|
||||
@@ -115,7 +115,7 @@ def update_template_group(
|
||||
group_id: int,
|
||||
group_data: OSTemplateGroupUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.manage"))
|
||||
current_user: User = Depends(PermissionChecker("template:manage"))
|
||||
):
|
||||
"""Обновить группу шаблонов"""
|
||||
group = db.query(OSTemplateGroup).filter(OSTemplateGroup.id == group_id).first()
|
||||
@@ -137,7 +137,7 @@ def update_template_group(
|
||||
def delete_template_group(
|
||||
group_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.manage"))
|
||||
current_user: User = Depends(PermissionChecker("template:manage"))
|
||||
):
|
||||
"""Удалить группу шаблонов"""
|
||||
group = db.query(OSTemplateGroup).filter(OSTemplateGroup.id == group_id).first()
|
||||
@@ -163,7 +163,7 @@ def delete_template_group(
|
||||
@router.get("/api/templates", response_model=List[OSTemplateWithGroup])
|
||||
def list_os_templates(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.view")),
|
||||
current_user: User = Depends(PermissionChecker("template:view")),
|
||||
group_id: int = None,
|
||||
server_id: int = None,
|
||||
vm_type: Optional[str] = None,
|
||||
@@ -206,7 +206,7 @@ def list_os_templates(
|
||||
def create_os_template(
|
||||
template_data: OSTemplateCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.manage"))
|
||||
current_user: User = Depends(PermissionChecker("template:manage"))
|
||||
):
|
||||
"""Создать новый OS шаблон"""
|
||||
# Validate group exists
|
||||
@@ -243,7 +243,7 @@ def create_os_template(
|
||||
def get_os_template(
|
||||
template_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.view"))
|
||||
current_user: User = Depends(PermissionChecker("template:view"))
|
||||
):
|
||||
"""Получить OS шаблон по ID"""
|
||||
template = db.query(OSTemplate).filter(OSTemplate.id == template_id).first()
|
||||
@@ -269,7 +269,7 @@ def update_os_template(
|
||||
template_id: int,
|
||||
template_data: OSTemplateUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.manage"))
|
||||
current_user: User = Depends(PermissionChecker("template:manage"))
|
||||
):
|
||||
"""Обновить OS шаблон"""
|
||||
template = db.query(OSTemplate).filter(OSTemplate.id == template_id).first()
|
||||
@@ -304,7 +304,7 @@ def update_os_template(
|
||||
def delete_os_template(
|
||||
template_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.manage"))
|
||||
current_user: User = Depends(PermissionChecker("template:manage"))
|
||||
):
|
||||
"""Удалить OS шаблон"""
|
||||
template = db.query(OSTemplate).filter(OSTemplate.id == template_id).first()
|
||||
@@ -323,7 +323,7 @@ def delete_os_template(
|
||||
def discover_proxmox_templates(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.view"))
|
||||
current_user: User = Depends(PermissionChecker("template:view"))
|
||||
):
|
||||
"""
|
||||
Обнаружить все VM-шаблоны на Proxmox сервере.
|
||||
@@ -562,7 +562,7 @@ def _do_auto_import(server_id: int, username: str):
|
||||
def auto_import_templates(
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.manage"))
|
||||
current_user: User = Depends(PermissionChecker("template:manage"))
|
||||
):
|
||||
"""
|
||||
Синхронный импорт шаблонов с Proxmox сервера.
|
||||
@@ -1006,7 +1006,7 @@ def _do_deploy_sync(task_id: int, deploy_data: VMDeployRequest,
|
||||
async def deploy_vm_from_template(
|
||||
deploy_data: VMDeployRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("vms.create"))
|
||||
current_user: User = Depends(PermissionChecker("vm:create"))
|
||||
):
|
||||
"""
|
||||
Асинхронный деплой VM из шаблона.
|
||||
@@ -1034,7 +1034,7 @@ async def deploy_vm_from_template(
|
||||
raise HTTPException(status_code=400, detail=f"Минимум {template.min_disk} GB диска")
|
||||
|
||||
# Resolve owner / SSH keys (admin may target another user)
|
||||
is_admin = current_user.has_permission("users.manage") or current_user.is_admin
|
||||
is_admin = current_user.has_permission("user:manage") or current_user.is_admin
|
||||
instance_owner_id = current_user.id
|
||||
if deploy_data.owner_id and deploy_data.owner_id != current_user.id:
|
||||
if not is_admin:
|
||||
@@ -1105,7 +1105,7 @@ async def get_deploy_task_status(
|
||||
def get_quick_deploy_info(
|
||||
template_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("templates.view"))
|
||||
current_user: User = Depends(PermissionChecker("template:view"))
|
||||
):
|
||||
"""Получить информацию для быстрого развертывания VM"""
|
||||
template = db.query(OSTemplate).filter(OSTemplate.id == template_id).first()
|
||||
|
||||
@@ -120,7 +120,7 @@ class SecuritySettingsUpdate(BaseModel):
|
||||
@router.get("/api/roles", response_model=List[RoleResponse])
|
||||
async def list_roles(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("roles.view"))
|
||||
current_user: User = Depends(PermissionChecker("role:view"))
|
||||
):
|
||||
"""Get all roles"""
|
||||
from sqlalchemy import func
|
||||
@@ -154,7 +154,7 @@ async def list_roles(
|
||||
async def get_role(
|
||||
role_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("roles.view"))
|
||||
current_user: User = Depends(PermissionChecker("role:view"))
|
||||
):
|
||||
"""Get role by ID"""
|
||||
role = db.query(Role).filter(Role.id == role_id).first()
|
||||
@@ -168,7 +168,7 @@ async def create_role(
|
||||
role_data: RoleCreate,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("roles.manage"))
|
||||
current_user: User = Depends(PermissionChecker("role:manage"))
|
||||
):
|
||||
"""Create a new role"""
|
||||
# Check if role name exists
|
||||
@@ -207,7 +207,7 @@ async def update_role(
|
||||
role_data: RoleUpdate,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("roles.manage"))
|
||||
current_user: User = Depends(PermissionChecker("role:manage"))
|
||||
):
|
||||
"""Update a role"""
|
||||
role = db.query(Role).filter(Role.id == role_id).first()
|
||||
@@ -250,7 +250,7 @@ async def delete_role(
|
||||
role_id: int,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("roles.manage"))
|
||||
current_user: User = Depends(PermissionChecker("role:manage"))
|
||||
):
|
||||
"""Delete a role"""
|
||||
success, message = SecurityService.delete_role(db, role_id)
|
||||
@@ -275,7 +275,7 @@ async def delete_role(
|
||||
|
||||
@router.get("/api/permissions/v2")
|
||||
async def get_all_permissions_v2(
|
||||
current_user: User = Depends(PermissionChecker("roles.view"))
|
||||
current_user: User = Depends(PermissionChecker("role:view"))
|
||||
):
|
||||
"""
|
||||
Get all available permissions in new format.
|
||||
@@ -289,7 +289,7 @@ async def get_all_permissions_v2(
|
||||
@router.get("/api/users", response_model=List[UserResponse])
|
||||
async def list_users(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.view"))
|
||||
current_user: User = Depends(PermissionChecker("user:view"))
|
||||
):
|
||||
"""Get all users"""
|
||||
users = db.query(User).order_by(User.username).all()
|
||||
@@ -320,7 +320,7 @@ async def list_users(
|
||||
async def get_user(
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.view"))
|
||||
current_user: User = Depends(PermissionChecker("user:view"))
|
||||
):
|
||||
"""Get user by ID"""
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
@@ -348,7 +348,7 @@ async def create_user(
|
||||
user_data: UserCreate,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.create"))
|
||||
current_user: User = Depends(PermissionChecker("user:create"))
|
||||
):
|
||||
"""Create a new user"""
|
||||
# Check username
|
||||
@@ -423,7 +423,7 @@ async def update_user(
|
||||
user_data: UserUpdate,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.edit"))
|
||||
current_user: User = Depends(PermissionChecker("user:update"))
|
||||
):
|
||||
"""Update a user"""
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
@@ -521,7 +521,7 @@ async def delete_user(
|
||||
user_id: int,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.delete"))
|
||||
current_user: User = Depends(PermissionChecker("user:delete"))
|
||||
):
|
||||
"""Delete a user"""
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
@@ -584,7 +584,7 @@ def _get_server_workspace_info(db: Session, server_id: int):
|
||||
async def get_user_servers(
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.view"))
|
||||
current_user: User = Depends(PermissionChecker("user:view"))
|
||||
):
|
||||
"""Get list of Proxmox servers assigned to a specific user"""
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
@@ -600,7 +600,7 @@ async def get_user_servers(
|
||||
async def get_user_server_assignments(
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.view"))
|
||||
current_user: User = Depends(PermissionChecker("user:view"))
|
||||
):
|
||||
"""
|
||||
Return all Proxmox servers enriched with:
|
||||
@@ -649,7 +649,7 @@ async def set_user_servers(
|
||||
user_id: int,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.edit"))
|
||||
current_user: User = Depends(PermissionChecker("user:update"))
|
||||
):
|
||||
"""Assign a list of Proxmox servers to a user (replaces current assignment).
|
||||
Rejects the operation if any server's workspaces don't intersect with the user's workspaces.
|
||||
@@ -723,7 +723,7 @@ async def reset_user_password(
|
||||
password_data: PasswordChange,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.edit"))
|
||||
current_user: User = Depends(PermissionChecker("user:update"))
|
||||
):
|
||||
"""Reset user password (admin action)"""
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
@@ -768,7 +768,7 @@ async def unlock_user(
|
||||
user_id: int,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.edit"))
|
||||
current_user: User = Depends(PermissionChecker("user:update"))
|
||||
):
|
||||
"""Unlock a locked user account"""
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
@@ -801,7 +801,7 @@ async def terminate_user_sessions(
|
||||
user_id: int,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.edit"))
|
||||
current_user: User = Depends(PermissionChecker("user:update"))
|
||||
):
|
||||
"""Terminate all sessions for a user"""
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
@@ -832,7 +832,7 @@ async def terminate_user_sessions(
|
||||
@router.get("/api/security/settings")
|
||||
async def get_security_settings(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.security"))
|
||||
current_user: User = Depends(PermissionChecker("setting:manage"))
|
||||
):
|
||||
"""Get security settings"""
|
||||
return {
|
||||
@@ -854,7 +854,7 @@ async def update_security_settings(
|
||||
settings_data: SecuritySettingsUpdate,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.security"))
|
||||
current_user: User = Depends(PermissionChecker("setting:manage"))
|
||||
):
|
||||
"""Update security settings"""
|
||||
updated = []
|
||||
@@ -883,7 +883,7 @@ async def update_security_settings(
|
||||
@router.get("/api/security/blocked-ips")
|
||||
async def list_blocked_ips(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.security"))
|
||||
current_user: User = Depends(PermissionChecker("setting:manage"))
|
||||
):
|
||||
"""Get list of blocked IPs"""
|
||||
from ..models import BlockedIP
|
||||
@@ -906,7 +906,7 @@ async def unblock_ip(
|
||||
ip_address: str,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.security"))
|
||||
current_user: User = Depends(PermissionChecker("setting:manage"))
|
||||
):
|
||||
"""Unblock an IP address"""
|
||||
if SecurityService.unblock_ip(db, ip_address):
|
||||
@@ -930,7 +930,7 @@ async def unblock_ip(
|
||||
@router.get("/api/sessions")
|
||||
async def list_sessions(
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.view"))
|
||||
current_user: User = Depends(PermissionChecker("user:view"))
|
||||
):
|
||||
"""Get list of active sessions"""
|
||||
from ..models import ActiveSession
|
||||
@@ -961,7 +961,7 @@ async def terminate_session(
|
||||
session_id: int,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.edit"))
|
||||
current_user: User = Depends(PermissionChecker("user:update"))
|
||||
):
|
||||
"""Terminate a specific session"""
|
||||
from ..models import ActiveSession
|
||||
@@ -991,7 +991,7 @@ async def terminate_session(
|
||||
async def terminate_all_sessions(
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("users.edit"))
|
||||
current_user: User = Depends(PermissionChecker("user:update"))
|
||||
):
|
||||
"""Terminate all sessions except current user's"""
|
||||
from ..models import ActiveSession
|
||||
@@ -1022,7 +1022,7 @@ async def terminate_all_sessions(
|
||||
async def list_security_events(
|
||||
limit: int = 50,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.security"))
|
||||
current_user: User = Depends(PermissionChecker("setting:manage"))
|
||||
):
|
||||
"""Get security events from logs"""
|
||||
from ..models import AuditLog
|
||||
@@ -1049,7 +1049,7 @@ async def block_ip(
|
||||
ip_data: dict,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.security"))
|
||||
current_user: User = Depends(PermissionChecker("setting:manage"))
|
||||
):
|
||||
"""Block an IP address manually"""
|
||||
from ..models import BlockedIP
|
||||
@@ -1103,7 +1103,7 @@ async def unblock_ip_by_id(
|
||||
blocked_id: int,
|
||||
request: Request,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.security"))
|
||||
current_user: User = Depends(PermissionChecker("setting:manage"))
|
||||
):
|
||||
"""Unblock an IP address by ID"""
|
||||
from ..models import BlockedIP
|
||||
|
||||
@@ -180,7 +180,7 @@ def list_workspaces(
|
||||
def create_workspace(
|
||||
data: WorkspaceCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.panel")),
|
||||
current_user: User = Depends(PermissionChecker("setting:update")),
|
||||
):
|
||||
"""Create a new workspace (admin only)."""
|
||||
existing = db.query(Workspace).filter(Workspace.name == data.name).first()
|
||||
@@ -259,7 +259,7 @@ def update_workspace(
|
||||
workspace_id: int,
|
||||
data: WorkspaceUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.panel")),
|
||||
current_user: User = Depends(PermissionChecker("setting:update")),
|
||||
):
|
||||
"""Update workspace name/description/color (admin only)."""
|
||||
ws = db.query(Workspace).filter(Workspace.id == workspace_id).first()
|
||||
@@ -288,7 +288,7 @@ def update_workspace(
|
||||
def delete_workspace(
|
||||
workspace_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.panel")),
|
||||
current_user: User = Depends(PermissionChecker("setting:update")),
|
||||
):
|
||||
"""Delete workspace (admin only). Default workspace cannot be deleted."""
|
||||
ws = db.query(Workspace).filter(Workspace.id == workspace_id).first()
|
||||
@@ -318,7 +318,7 @@ def assign_servers(
|
||||
workspace_id: int,
|
||||
body: _IdsRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.panel")),
|
||||
current_user: User = Depends(PermissionChecker("setting:update")),
|
||||
):
|
||||
"""Replace the server list of a workspace (admin only)."""
|
||||
ws = db.query(Workspace).filter(Workspace.id == workspace_id).first()
|
||||
@@ -344,7 +344,7 @@ def remove_server(
|
||||
workspace_id: int,
|
||||
server_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.panel")),
|
||||
current_user: User = Depends(PermissionChecker("setting:update")),
|
||||
):
|
||||
"""Remove a single server from a workspace (admin only)."""
|
||||
ws = db.query(Workspace).filter(Workspace.id == workspace_id).first()
|
||||
@@ -369,7 +369,7 @@ def assign_users(
|
||||
workspace_id: int,
|
||||
body: _IdsRequest,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.panel")),
|
||||
current_user: User = Depends(PermissionChecker("setting:update")),
|
||||
):
|
||||
"""Replace the user list of a workspace (admin only)."""
|
||||
ws = db.query(Workspace).filter(Workspace.id == workspace_id).first()
|
||||
@@ -393,7 +393,7 @@ def remove_user(
|
||||
workspace_id: int,
|
||||
user_id: int,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(PermissionChecker("settings.panel")),
|
||||
current_user: User = Depends(PermissionChecker("setting:update")),
|
||||
):
|
||||
"""Remove a single user from a workspace (admin only)."""
|
||||
ws = db.query(Workspace).filter(Workspace.id == workspace_id).first()
|
||||
|
||||
Reference in New Issue
Block a user