653 lines
20 KiB
Python
653 lines
20 KiB
Python
"""
|
|
Permission Registry - Central definition of all permissions
|
|
Format: resource:action[:scope]
|
|
|
|
Resources: cluster, node, vm, lxc, storage, network, backup, template, user, role, log, setting, ipam
|
|
Actions: view, list, create, update, delete, start, stop, restart, console, migrate, manage
|
|
Scopes: global, organization, workspace, instance (optional, defaults to global)
|
|
"""
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Dict, List, Optional, Set
|
|
from enum import Enum
|
|
|
|
|
|
class ResourceType(str, Enum):
|
|
"""Resource types in the system"""
|
|
CLUSTER = "cluster"
|
|
NODE = "node"
|
|
SERVER = "server" # Proxmox server
|
|
VM = "vm"
|
|
LXC = "lxc"
|
|
STORAGE = "storage"
|
|
NETWORK = "network"
|
|
BACKUP = "backup"
|
|
TEMPLATE = "template"
|
|
USER = "user"
|
|
ROLE = "role"
|
|
LOG = "log"
|
|
SETTING = "setting"
|
|
IPAM = "ipam"
|
|
NOTIFICATION = "notification"
|
|
DASHBOARD = "dashboard"
|
|
|
|
|
|
class ActionType(str, Enum):
|
|
"""Action types"""
|
|
VIEW = "view"
|
|
LIST = "list"
|
|
CREATE = "create"
|
|
UPDATE = "update"
|
|
DELETE = "delete"
|
|
START = "start"
|
|
STOP = "stop"
|
|
RESTART = "restart"
|
|
CONSOLE = "console"
|
|
MIGRATE = "migrate"
|
|
MANAGE = "manage"
|
|
EXPORT = "export"
|
|
EXECUTE = "execute" # For running commands
|
|
|
|
|
|
class ScopeType(str, Enum):
|
|
"""Permission scope levels"""
|
|
GLOBAL = "global" # Platform-wide access
|
|
ORGANIZATION = "org" # Within organization
|
|
WORKSPACE = "workspace" # Within workspace
|
|
INSTANCE = "instance" # Specific resource only
|
|
|
|
|
|
@dataclass
|
|
class Permission:
|
|
"""
|
|
Atomic permission definition.
|
|
|
|
Format: resource:action or resource:action:scope
|
|
Examples:
|
|
- vm:view (global scope)
|
|
- vm:create:workspace (workspace scope)
|
|
- vm:delete:instance (instance scope)
|
|
"""
|
|
resource: str
|
|
action: str
|
|
scope: ScopeType = ScopeType.GLOBAL
|
|
display_name: str = ""
|
|
description: str = ""
|
|
category: str = ""
|
|
requires: List[str] = field(default_factory=list) # Dependencies
|
|
|
|
@property
|
|
def code(self) -> str:
|
|
"""Get permission code"""
|
|
if self.scope == ScopeType.GLOBAL:
|
|
return f"{self.resource}:{self.action}"
|
|
return f"{self.resource}:{self.action}:{self.scope.value}"
|
|
|
|
@property
|
|
def legacy_code(self) -> str:
|
|
"""Get legacy permission code for backwards compatibility"""
|
|
# Map to old format: resource.action
|
|
return f"{self.resource}.{self.action}"
|
|
|
|
def __hash__(self):
|
|
return hash(self.code)
|
|
|
|
def __eq__(self, other):
|
|
if isinstance(other, Permission):
|
|
return self.code == other.code
|
|
return False
|
|
|
|
|
|
class PermissionRegistry:
|
|
"""
|
|
Central registry of all permissions.
|
|
Provides validation, lookup, and category grouping.
|
|
"""
|
|
|
|
_instance = None
|
|
_permissions: Dict[str, Permission] = {}
|
|
_by_category: Dict[str, List[Permission]] = {}
|
|
_by_resource: Dict[str, List[Permission]] = {}
|
|
_legacy_map: Dict[str, str] = {} # legacy code -> new code
|
|
|
|
def __new__(cls):
|
|
if cls._instance is None:
|
|
cls._instance = super().__new__(cls)
|
|
cls._instance._initialized = False
|
|
return cls._instance
|
|
|
|
def __init__(self):
|
|
if self._initialized:
|
|
return
|
|
self._initialized = True
|
|
self._permissions = {}
|
|
self._by_category = {}
|
|
self._by_resource = {}
|
|
self._legacy_map = {}
|
|
|
|
def register(self, permission: Permission) -> None:
|
|
"""Register a permission"""
|
|
self._permissions[permission.code] = permission
|
|
|
|
# Index by category
|
|
if permission.category not in self._by_category:
|
|
self._by_category[permission.category] = []
|
|
self._by_category[permission.category].append(permission)
|
|
|
|
# Index by resource
|
|
if permission.resource not in self._by_resource:
|
|
self._by_resource[permission.resource] = []
|
|
self._by_resource[permission.resource].append(permission)
|
|
|
|
# Legacy mapping
|
|
self._legacy_map[permission.legacy_code] = permission.code
|
|
|
|
def get(self, code: str) -> Optional[Permission]:
|
|
"""Get permission by code"""
|
|
# Try direct lookup
|
|
if code in self._permissions:
|
|
return self._permissions[code]
|
|
# Try legacy format
|
|
if code in self._legacy_map:
|
|
return self._permissions[self._legacy_map[code]]
|
|
return None
|
|
|
|
def exists(self, code: str) -> bool:
|
|
"""Check if permission exists"""
|
|
return code in self._permissions or code in self._legacy_map
|
|
|
|
def resolve(self, code: str) -> Optional[str]:
|
|
"""Resolve legacy code to new code"""
|
|
if code in self._permissions:
|
|
return code
|
|
return self._legacy_map.get(code)
|
|
|
|
def all(self) -> List[Permission]:
|
|
"""Get all permissions"""
|
|
return list(self._permissions.values())
|
|
|
|
def by_category(self, category: str) -> List[Permission]:
|
|
"""Get permissions by category"""
|
|
return self._by_category.get(category, [])
|
|
|
|
def by_resource(self, resource: str) -> List[Permission]:
|
|
"""Get permissions by resource"""
|
|
return self._by_resource.get(resource, [])
|
|
|
|
def categories(self) -> List[str]:
|
|
"""Get all categories"""
|
|
return list(self._by_category.keys())
|
|
|
|
def resources(self) -> List[str]:
|
|
"""Get all resources"""
|
|
return list(self._by_resource.keys())
|
|
|
|
def validate(self, codes: List[str]) -> tuple[List[str], List[str]]:
|
|
"""Validate permission codes, returns (valid, invalid)"""
|
|
valid = []
|
|
invalid = []
|
|
for code in codes:
|
|
if self.exists(code):
|
|
valid.append(self.resolve(code) or code)
|
|
else:
|
|
invalid.append(code)
|
|
return valid, invalid
|
|
|
|
|
|
# ==================== Permission Definitions ====================
|
|
|
|
def _create_permissions() -> PermissionRegistry:
|
|
"""Create and populate the permission registry"""
|
|
registry = PermissionRegistry()
|
|
|
|
# Dashboard permissions
|
|
registry.register(Permission(
|
|
resource="dashboard", action="view",
|
|
display_name="View Dashboard",
|
|
description="Access to dashboard and overview",
|
|
category="Dashboard"
|
|
))
|
|
|
|
# Server (Proxmox) permissions
|
|
registry.register(Permission(
|
|
resource="server", action="view",
|
|
display_name="View Servers",
|
|
description="View Proxmox server list and status",
|
|
category="Proxmox Servers"
|
|
))
|
|
registry.register(Permission(
|
|
resource="server", action="create",
|
|
display_name="Add Server",
|
|
description="Add new Proxmox servers",
|
|
category="Proxmox Servers"
|
|
))
|
|
registry.register(Permission(
|
|
resource="server", action="update",
|
|
display_name="Edit Server",
|
|
description="Edit Proxmox server settings",
|
|
category="Proxmox Servers"
|
|
))
|
|
registry.register(Permission(
|
|
resource="server", action="delete",
|
|
display_name="Delete Server",
|
|
description="Remove Proxmox servers",
|
|
category="Proxmox Servers"
|
|
))
|
|
registry.register(Permission(
|
|
resource="server", action="manage",
|
|
display_name="Manage Servers",
|
|
description="Full server management including cluster operations",
|
|
category="Proxmox Servers",
|
|
requires=["server:view"]
|
|
))
|
|
|
|
# VM permissions
|
|
registry.register(Permission(
|
|
resource="vm", action="view",
|
|
display_name="View VMs",
|
|
description="View virtual machines list and details",
|
|
category="Virtual Machines"
|
|
))
|
|
registry.register(Permission(
|
|
resource="vm", action="create",
|
|
display_name="Create VMs",
|
|
description="Create new virtual machines",
|
|
category="Virtual Machines",
|
|
requires=["vm:view", "template:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="vm", action="update",
|
|
display_name="Update VMs",
|
|
description="Modify VM configuration",
|
|
category="Virtual Machines",
|
|
requires=["vm:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="vm", action="delete",
|
|
display_name="Delete VMs",
|
|
description="Delete virtual machines",
|
|
category="Virtual Machines",
|
|
requires=["vm:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="vm", action="start",
|
|
display_name="Start VMs",
|
|
description="Start virtual machines",
|
|
category="Virtual Machines",
|
|
requires=["vm:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="vm", action="stop",
|
|
display_name="Stop VMs",
|
|
description="Stop virtual machines",
|
|
category="Virtual Machines",
|
|
requires=["vm:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="vm", action="restart",
|
|
display_name="Restart VMs",
|
|
description="Restart virtual machines",
|
|
category="Virtual Machines",
|
|
requires=["vm:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="vm", action="console",
|
|
display_name="VM Console",
|
|
description="Access VM console (noVNC/xterm.js)",
|
|
category="Virtual Machines",
|
|
requires=["vm:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="vm", action="migrate",
|
|
display_name="Migrate VMs",
|
|
description="Migrate VMs between nodes",
|
|
category="Virtual Machines",
|
|
requires=["vm:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="vm", action="execute",
|
|
display_name="Execute Commands",
|
|
description="Execute commands on VMs via QEMU agent",
|
|
category="Virtual Machines",
|
|
requires=["vm:console"]
|
|
))
|
|
|
|
# LXC (Container) permissions - same structure as VM
|
|
registry.register(Permission(
|
|
resource="lxc", action="view",
|
|
display_name="View Containers",
|
|
description="View LXC containers list and details",
|
|
category="Containers"
|
|
))
|
|
registry.register(Permission(
|
|
resource="lxc", action="create",
|
|
display_name="Create Containers",
|
|
description="Create new LXC containers",
|
|
category="Containers",
|
|
requires=["lxc:view", "template:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="lxc", action="update",
|
|
display_name="Update Containers",
|
|
description="Modify container configuration",
|
|
category="Containers",
|
|
requires=["lxc:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="lxc", action="delete",
|
|
display_name="Delete Containers",
|
|
description="Delete LXC containers",
|
|
category="Containers",
|
|
requires=["lxc:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="lxc", action="start",
|
|
display_name="Start Containers",
|
|
description="Start LXC containers",
|
|
category="Containers",
|
|
requires=["lxc:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="lxc", action="stop",
|
|
display_name="Stop Containers",
|
|
description="Stop LXC containers",
|
|
category="Containers",
|
|
requires=["lxc:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="lxc", action="restart",
|
|
display_name="Restart Containers",
|
|
description="Restart LXC containers",
|
|
category="Containers",
|
|
requires=["lxc:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="lxc", action="console",
|
|
display_name="Container Console",
|
|
description="Access container console",
|
|
category="Containers",
|
|
requires=["lxc:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="lxc", action="migrate",
|
|
display_name="Migrate Containers",
|
|
description="Migrate containers between nodes",
|
|
category="Containers",
|
|
requires=["lxc:view"]
|
|
))
|
|
|
|
# Template permissions
|
|
registry.register(Permission(
|
|
resource="template", action="view",
|
|
display_name="View Templates",
|
|
description="View OS templates",
|
|
category="Templates"
|
|
))
|
|
registry.register(Permission(
|
|
resource="template", action="create",
|
|
display_name="Create Templates",
|
|
description="Create new templates",
|
|
category="Templates",
|
|
requires=["template:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="template", action="update",
|
|
display_name="Update Templates",
|
|
description="Edit template settings",
|
|
category="Templates",
|
|
requires=["template:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="template", action="delete",
|
|
display_name="Delete Templates",
|
|
description="Delete templates",
|
|
category="Templates",
|
|
requires=["template:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="template", action="manage",
|
|
display_name="Manage Templates",
|
|
description="Full template management including download",
|
|
category="Templates",
|
|
requires=["template:view"]
|
|
))
|
|
|
|
# Storage permissions
|
|
registry.register(Permission(
|
|
resource="storage", action="view",
|
|
display_name="View Storage",
|
|
description="View storage pools and usage",
|
|
category="Storage"
|
|
))
|
|
registry.register(Permission(
|
|
resource="storage", action="manage",
|
|
display_name="Manage Storage",
|
|
description="Manage storage pools and volumes",
|
|
category="Storage",
|
|
requires=["storage:view"]
|
|
))
|
|
|
|
# Backup permissions
|
|
registry.register(Permission(
|
|
resource="backup", action="view",
|
|
display_name="View Backups",
|
|
description="View backup list",
|
|
category="Backups"
|
|
))
|
|
registry.register(Permission(
|
|
resource="backup", action="create",
|
|
display_name="Create Backups",
|
|
description="Create new backups",
|
|
category="Backups",
|
|
requires=["backup:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="backup", action="delete",
|
|
display_name="Delete Backups",
|
|
description="Delete backups",
|
|
category="Backups",
|
|
requires=["backup:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="backup", action="manage",
|
|
display_name="Restore Backups",
|
|
description="Restore from backups",
|
|
category="Backups",
|
|
requires=["backup:view", "vm:create"]
|
|
))
|
|
|
|
# IPAM permissions
|
|
registry.register(Permission(
|
|
resource="ipam", action="view",
|
|
display_name="View IPAM",
|
|
description="View IP address management",
|
|
category="IPAM"
|
|
))
|
|
registry.register(Permission(
|
|
resource="ipam", action="manage",
|
|
display_name="Manage IPAM",
|
|
description="Manage networks and IP allocations",
|
|
category="IPAM",
|
|
requires=["ipam:view"]
|
|
))
|
|
|
|
# User management permissions
|
|
registry.register(Permission(
|
|
resource="user", action="view",
|
|
display_name="View Users",
|
|
description="View user list",
|
|
category="User Management"
|
|
))
|
|
registry.register(Permission(
|
|
resource="user", action="create",
|
|
display_name="Create Users",
|
|
description="Create new users",
|
|
category="User Management",
|
|
requires=["user:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="user", action="update",
|
|
display_name="Update Users",
|
|
description="Edit user profiles",
|
|
category="User Management",
|
|
requires=["user:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="user", action="delete",
|
|
display_name="Delete Users",
|
|
description="Delete users",
|
|
category="User Management",
|
|
requires=["user:view"]
|
|
))
|
|
|
|
# Role management permissions
|
|
registry.register(Permission(
|
|
resource="role", action="view",
|
|
display_name="View Roles",
|
|
description="View roles and permissions",
|
|
category="Role Management"
|
|
))
|
|
registry.register(Permission(
|
|
resource="role", action="create",
|
|
display_name="Create Roles",
|
|
description="Create new roles",
|
|
category="Role Management",
|
|
requires=["role:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="role", action="update",
|
|
display_name="Update Roles",
|
|
description="Edit role permissions",
|
|
category="Role Management",
|
|
requires=["role:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="role", action="delete",
|
|
display_name="Delete Roles",
|
|
description="Delete roles",
|
|
category="Role Management",
|
|
requires=["role:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="role", action="manage",
|
|
display_name="Manage Roles",
|
|
description="Full role management including assignment",
|
|
category="Role Management",
|
|
requires=["role:view", "user:view"]
|
|
))
|
|
|
|
# Log permissions
|
|
registry.register(Permission(
|
|
resource="log", action="view",
|
|
display_name="View Logs",
|
|
description="View audit logs",
|
|
category="Logs"
|
|
))
|
|
registry.register(Permission(
|
|
resource="log", action="export",
|
|
display_name="Export Logs",
|
|
description="Export log data",
|
|
category="Logs",
|
|
requires=["log:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="log", action="delete",
|
|
display_name="Delete Logs",
|
|
description="Delete log entries",
|
|
category="Logs",
|
|
requires=["log:view"]
|
|
))
|
|
|
|
# Settings permissions
|
|
registry.register(Permission(
|
|
resource="setting", action="view",
|
|
display_name="View Settings",
|
|
description="View panel settings",
|
|
category="Settings"
|
|
))
|
|
registry.register(Permission(
|
|
resource="setting", action="update",
|
|
display_name="Update Settings",
|
|
description="Modify panel settings",
|
|
category="Settings",
|
|
requires=["setting:view"]
|
|
))
|
|
registry.register(Permission(
|
|
resource="setting", action="manage",
|
|
display_name="Manage Security Settings",
|
|
description="Manage security and advanced settings",
|
|
category="Settings",
|
|
requires=["setting:view"]
|
|
))
|
|
|
|
# Notification permissions
|
|
registry.register(Permission(
|
|
resource="notification", action="view",
|
|
display_name="View Notifications",
|
|
description="View notifications",
|
|
category="Notifications"
|
|
))
|
|
registry.register(Permission(
|
|
resource="notification", action="manage",
|
|
display_name="Manage Notifications",
|
|
description="Manage notification settings",
|
|
category="Notifications",
|
|
requires=["notification:view"]
|
|
))
|
|
|
|
return registry
|
|
|
|
|
|
# Global registry instance
|
|
PERMISSIONS = _create_permissions()
|
|
|
|
|
|
# ==================== Legacy Permission Mapping ====================
|
|
|
|
LEGACY_PERMISSION_MAP = {
|
|
# Old format -> New format
|
|
"dashboard.view": "dashboard:view",
|
|
"proxmox.view": "server:view",
|
|
"proxmox.manage": "server:manage",
|
|
"proxmox.servers.add": "server:create",
|
|
"proxmox.servers.edit": "server:update",
|
|
"proxmox.servers.delete": "server:delete",
|
|
"proxmox.vm.manage": "vm:manage",
|
|
"vms.view": "vm:view",
|
|
"vms.create": "vm:create",
|
|
"vms.start": "vm:start",
|
|
"vms.stop": "vm:stop",
|
|
"vms.restart": "vm:restart",
|
|
"vms.delete": "vm:delete",
|
|
"vms.console": "vm:console",
|
|
"vms.migrate": "vm:migrate",
|
|
"templates.view": "template:view",
|
|
"templates.manage": "template:manage",
|
|
"ipam.view": "ipam:view",
|
|
"ipam.manage": "ipam:manage",
|
|
"logs.view": "log:view",
|
|
"logs.export": "log:export",
|
|
"logs.delete": "log:delete",
|
|
"settings.view": "setting:view",
|
|
"settings.panel": "setting:update",
|
|
"settings.security": "setting:manage",
|
|
"users.view": "user:view",
|
|
"users.create": "user:create",
|
|
"users.edit": "user:update",
|
|
"users.delete": "user:delete",
|
|
"roles.view": "role:view",
|
|
"roles.manage": "role:manage",
|
|
"notifications.manage": "notification:manage",
|
|
}
|
|
|
|
|
|
def resolve_permission(code: str) -> str:
|
|
"""Resolve legacy permission code to new format"""
|
|
return LEGACY_PERMISSION_MAP.get(code, code)
|
|
|
|
|
|
def get_permission_info(code: str) -> Optional[Permission]:
|
|
"""Get permission object by code (supports legacy)"""
|
|
resolved = resolve_permission(code)
|
|
return PERMISSIONS.get(resolved)
|