423 lines
15 KiB
Python
423 lines
15 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
"""
|
|
System Module - Main Module.
|
|
Registers system-level RBAC objects (UI, DATA, RESOURCE) that are not part of any feature.
|
|
These are global system pages and resources available to all users with appropriate roles.
|
|
|
|
Also defines the navigation structure for the frontend.
|
|
"""
|
|
|
|
import logging
|
|
from typing import Dict, List, Any, Optional
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# System metadata
|
|
FEATURE_CODE = "system"
|
|
FEATURE_LABEL = {"en": "System", "de": "System", "fr": "Système"}
|
|
FEATURE_ICON = "mdi-cog"
|
|
|
|
# =============================================================================
|
|
# Navigation Structure (Single Source of Truth)
|
|
# =============================================================================
|
|
#
|
|
# Block Order (gemäss Navigation-API-Konzept):
|
|
# - System: 10
|
|
# - <dynamic/features>: 15 (wird in routeSystem.py eingefügt)
|
|
# - Workflows: 20
|
|
# - Basisdaten: 30
|
|
# - Migrate: 40
|
|
# - Administration: 200
|
|
#
|
|
# Item Order: Default-Abstand 10 pro Item
|
|
# uiComponent: Abgeleitet von objectKey (ui.system.home -> page.system.home)
|
|
# icon: Wird intern gehalten aber NICHT in der API Response zurückgegeben
|
|
|
|
NAVIGATION_SECTIONS = [
|
|
{
|
|
"id": "system",
|
|
"title": {"en": "SYSTEM", "de": "SYSTEM", "fr": "SYSTÈME"},
|
|
"order": 10,
|
|
"items": [
|
|
{
|
|
"id": "home",
|
|
"objectKey": "ui.system.home",
|
|
"label": {"en": "Home", "de": "Übersicht", "fr": "Accueil"},
|
|
"icon": "FaHome",
|
|
"path": "/",
|
|
"order": 10,
|
|
"public": True,
|
|
},
|
|
{
|
|
"id": "settings",
|
|
"objectKey": "ui.system.settings",
|
|
"label": {"en": "Settings", "de": "Einstellungen", "fr": "Paramètres"},
|
|
"icon": "FaCog",
|
|
"path": "/settings",
|
|
"order": 20,
|
|
"public": True,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"id": "workflows",
|
|
"title": {"en": "WORKFLOWS", "de": "WORKFLOWS", "fr": "WORKFLOWS"},
|
|
"order": 20,
|
|
"items": [
|
|
{
|
|
"id": "playground",
|
|
"objectKey": "ui.system.playground",
|
|
"label": {"en": "Chat Playground", "de": "Chat Playground", "fr": "Chat Playground"},
|
|
"icon": "FaPlay",
|
|
"path": "/workflows/playground",
|
|
"order": 10,
|
|
},
|
|
{
|
|
"id": "chats",
|
|
"objectKey": "ui.system.chats",
|
|
"label": {"en": "Chats", "de": "Chats", "fr": "Chats"},
|
|
"icon": "FaListAlt",
|
|
"path": "/workflows/list",
|
|
"order": 20,
|
|
},
|
|
{
|
|
"id": "automations",
|
|
"objectKey": "ui.system.automations",
|
|
"label": {"en": "Automations", "de": "Automatisierungen", "fr": "Automatisations"},
|
|
"icon": "FaCogs",
|
|
"path": "/workflows/automations",
|
|
"order": 30,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"id": "basedata",
|
|
"title": {"en": "BASE DATA", "de": "BASISDATEN", "fr": "DONNÉES DE BASE"},
|
|
"order": 30,
|
|
"items": [
|
|
{
|
|
"id": "prompts",
|
|
"objectKey": "ui.system.prompts",
|
|
"label": {"en": "Prompts", "de": "Prompts", "fr": "Prompts"},
|
|
"icon": "FaLightbulb",
|
|
"path": "/basedata/prompts",
|
|
"order": 10,
|
|
},
|
|
{
|
|
"id": "files",
|
|
"objectKey": "ui.system.files",
|
|
"label": {"en": "Files", "de": "Dateien", "fr": "Fichiers"},
|
|
"icon": "FaRegFileAlt",
|
|
"path": "/basedata/files",
|
|
"order": 20,
|
|
},
|
|
{
|
|
"id": "connections",
|
|
"objectKey": "ui.system.connections",
|
|
"label": {"en": "Connections", "de": "Verbindungen", "fr": "Connexions"},
|
|
"icon": "FaLink",
|
|
"path": "/basedata/connections",
|
|
"order": 30,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"id": "migrate",
|
|
"title": {"en": "MIGRATE TO FEATURES", "de": "MIGRATE TO FEATURES", "fr": "MIGRER VERS FEATURES"},
|
|
"order": 40,
|
|
"deprecated": True,
|
|
"items": [
|
|
{
|
|
"id": "chatbot",
|
|
"objectKey": "ui.system.chatbot",
|
|
"label": {"en": "Chatbot", "de": "Chatbot", "fr": "Chatbot"},
|
|
"icon": "FaComments",
|
|
"path": "/chatbot",
|
|
"order": 10,
|
|
"deprecated": True,
|
|
},
|
|
{
|
|
"id": "pek",
|
|
"objectKey": "ui.system.pek",
|
|
"label": {"en": "PEK", "de": "PEK", "fr": "PEK"},
|
|
"icon": "FaChartBar",
|
|
"path": "/pek",
|
|
"order": 20,
|
|
"deprecated": True,
|
|
},
|
|
{
|
|
"id": "speech",
|
|
"objectKey": "ui.system.speech",
|
|
"label": {"en": "Speech", "de": "Sprache", "fr": "Parole"},
|
|
"icon": "FaMicrophone",
|
|
"path": "/speech",
|
|
"order": 30,
|
|
"deprecated": True,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
"id": "admin",
|
|
"title": {"en": "ADMINISTRATION", "de": "ADMINISTRATION", "fr": "ADMINISTRATION"},
|
|
"order": 200,
|
|
"adminOnly": True,
|
|
"items": [
|
|
{
|
|
"id": "admin-users",
|
|
"objectKey": "ui.admin.users",
|
|
"label": {"en": "Users", "de": "Benutzer", "fr": "Utilisateurs"},
|
|
"icon": "FaUsers",
|
|
"path": "/admin/users",
|
|
"order": 10,
|
|
"adminOnly": True,
|
|
},
|
|
{
|
|
"id": "admin-invitations",
|
|
"objectKey": "ui.admin.invitations",
|
|
"label": {"en": "Invitations", "de": "Einladungen", "fr": "Invitations"},
|
|
"icon": "FaEnvelopeOpenText",
|
|
"path": "/admin/invitations",
|
|
"order": 20,
|
|
"adminOnly": True,
|
|
},
|
|
{
|
|
"id": "admin-mandates",
|
|
"objectKey": "ui.admin.mandates",
|
|
"label": {"en": "Mandates", "de": "Mandanten", "fr": "Mandats"},
|
|
"icon": "FaBuilding",
|
|
"path": "/admin/mandates",
|
|
"order": 30,
|
|
"adminOnly": True,
|
|
},
|
|
{
|
|
"id": "admin-roles",
|
|
"objectKey": "ui.admin.roles",
|
|
"label": {"en": "Roles", "de": "Rollen", "fr": "Rôles"},
|
|
"icon": "FaKey",
|
|
"path": "/admin/mandate-roles",
|
|
"order": 40,
|
|
"adminOnly": True,
|
|
},
|
|
{
|
|
"id": "admin-role-permissions",
|
|
"objectKey": "ui.admin.role-permissions",
|
|
"label": {"en": "Role Permissions", "de": "Rollen-Berechtigungen", "fr": "Permissions des rôles"},
|
|
"icon": "FaShieldAlt",
|
|
"path": "/admin/mandate-role-permissions",
|
|
"order": 50,
|
|
"adminOnly": True,
|
|
},
|
|
{
|
|
"id": "admin-user-mandates",
|
|
"objectKey": "ui.admin.user-mandates",
|
|
"label": {"en": "Mandate Members", "de": "Mandanten-Mitglieder", "fr": "Membres du mandat"},
|
|
"icon": "FaUserTag",
|
|
"path": "/admin/user-mandates",
|
|
"order": 60,
|
|
"adminOnly": True,
|
|
},
|
|
{
|
|
"id": "admin-feature-roles",
|
|
"objectKey": "ui.admin.feature-roles",
|
|
"label": {"en": "Feature Roles & Permissions", "de": "Feature Rollen & Rechte", "fr": "Rôles et permissions des features"},
|
|
"icon": "FaCube",
|
|
"path": "/admin/feature-roles",
|
|
"order": 70,
|
|
"adminOnly": True,
|
|
},
|
|
{
|
|
"id": "admin-feature-instances",
|
|
"objectKey": "ui.admin.feature-instances",
|
|
"label": {"en": "Feature Instances", "de": "Feature-Instanzen", "fr": "Instances de feature"},
|
|
"icon": "FaCubes",
|
|
"path": "/admin/feature-instances",
|
|
"order": 80,
|
|
"adminOnly": True,
|
|
},
|
|
{
|
|
"id": "admin-feature-users",
|
|
"objectKey": "ui.admin.feature-users",
|
|
"label": {"en": "Feature Instance Users", "de": "Feature Instanz Benutzer", "fr": "Utilisateurs d'instance de feature"},
|
|
"icon": "FaUsersCog",
|
|
"path": "/admin/feature-users",
|
|
"order": 90,
|
|
"adminOnly": True,
|
|
},
|
|
],
|
|
},
|
|
]
|
|
|
|
|
|
def _objectKeyToUiComponent(objectKey: str) -> str:
|
|
"""
|
|
Convert objectKey to uiComponent.
|
|
|
|
Example: ui.system.home -> page.system.home
|
|
ui.admin.users -> page.admin.users
|
|
ui.feature.trustee.dashboard -> page.feature.trustee.dashboard
|
|
"""
|
|
if objectKey.startswith("ui."):
|
|
return "page." + objectKey[3:]
|
|
return objectKey
|
|
|
|
|
|
def _buildUiObjectsFromNavigation() -> List[Dict[str, Any]]:
|
|
"""Build UI_OBJECTS list from NAVIGATION_SECTIONS for RBAC registration."""
|
|
uiObjects = []
|
|
for section in NAVIGATION_SECTIONS:
|
|
for item in section.get("items", []):
|
|
uiObjects.append({
|
|
"objectKey": item["objectKey"],
|
|
"label": item["label"],
|
|
"meta": {
|
|
"area": section["id"],
|
|
"public": item.get("public", False),
|
|
"adminOnly": item.get("adminOnly", False),
|
|
"deprecated": item.get("deprecated", False),
|
|
"path": item["path"],
|
|
"icon": item["icon"],
|
|
}
|
|
})
|
|
return uiObjects
|
|
|
|
|
|
# Generate UI_OBJECTS from navigation structure
|
|
UI_OBJECTS = _buildUiObjectsFromNavigation()
|
|
|
|
# =============================================================================
|
|
# System DATA Objects
|
|
# =============================================================================
|
|
|
|
DATA_OBJECTS = [
|
|
{
|
|
"objectKey": "data.system.User",
|
|
"label": {"en": "User", "de": "Benutzer", "fr": "Utilisateur"},
|
|
"meta": {"table": "UserInDB"}
|
|
},
|
|
{
|
|
"objectKey": "data.system.Mandate",
|
|
"label": {"en": "Mandate", "de": "Mandant", "fr": "Mandat"},
|
|
"meta": {"table": "Mandate"}
|
|
},
|
|
{
|
|
"objectKey": "data.system.Role",
|
|
"label": {"en": "Role", "de": "Rolle", "fr": "Rôle"},
|
|
"meta": {"table": "Role"}
|
|
},
|
|
{
|
|
"objectKey": "data.system.AccessRule",
|
|
"label": {"en": "Access Rule", "de": "Zugriffsregel", "fr": "Règle d'accès"},
|
|
"meta": {"table": "AccessRule"}
|
|
},
|
|
{
|
|
"objectKey": "data.system.UserMandate",
|
|
"label": {"en": "User Mandate", "de": "Benutzer-Mandant", "fr": "Mandat utilisateur"},
|
|
"meta": {"table": "UserMandate"}
|
|
},
|
|
{
|
|
"objectKey": "data.system.Prompt",
|
|
"label": {"en": "Prompt", "de": "Prompt", "fr": "Prompt"},
|
|
"meta": {"table": "Prompt"}
|
|
},
|
|
{
|
|
"objectKey": "data.system.ChatWorkflow",
|
|
"label": {"en": "Chat Workflow", "de": "Chat-Workflow", "fr": "Workflow de chat"},
|
|
"meta": {"table": "ChatWorkflow"}
|
|
},
|
|
{
|
|
"objectKey": "data.system.FileItem",
|
|
"label": {"en": "File", "de": "Datei", "fr": "Fichier"},
|
|
"meta": {"table": "FileItem"}
|
|
},
|
|
{
|
|
"objectKey": "data.system.UserConnection",
|
|
"label": {"en": "Connection", "de": "Verbindung", "fr": "Connexion"},
|
|
"meta": {"table": "UserConnection"}
|
|
},
|
|
{
|
|
"objectKey": "data.system.FeatureInstance",
|
|
"label": {"en": "Feature Instance", "de": "Feature-Instanz", "fr": "Instance de feature"},
|
|
"meta": {"table": "FeatureInstance"}
|
|
},
|
|
]
|
|
|
|
# =============================================================================
|
|
# System RESOURCE Objects
|
|
# =============================================================================
|
|
|
|
RESOURCE_OBJECTS = [
|
|
{
|
|
"objectKey": "resource.system.api.auth",
|
|
"label": {"en": "Authentication API", "de": "Authentifizierungs-API", "fr": "API d'authentification"},
|
|
"meta": {"endpoint": "/api/auth/*"}
|
|
},
|
|
{
|
|
"objectKey": "resource.system.api.users",
|
|
"label": {"en": "Users API", "de": "Benutzer-API", "fr": "API des utilisateurs"},
|
|
"meta": {"endpoint": "/api/users/*"}
|
|
},
|
|
{
|
|
"objectKey": "resource.system.api.mandates",
|
|
"label": {"en": "Mandates API", "de": "Mandanten-API", "fr": "API des mandats"},
|
|
"meta": {"endpoint": "/api/mandates/*"}
|
|
},
|
|
{
|
|
"objectKey": "resource.system.api.rbac",
|
|
"label": {"en": "RBAC API", "de": "RBAC-API", "fr": "API RBAC"},
|
|
"meta": {"endpoint": "/api/rbac/*"}
|
|
},
|
|
]
|
|
|
|
|
|
def registerFeature(catalogService) -> bool:
|
|
"""
|
|
Register system RBAC objects in the catalog.
|
|
|
|
Args:
|
|
catalogService: The RBAC catalog service instance
|
|
|
|
Returns:
|
|
True if registration was successful
|
|
"""
|
|
try:
|
|
# Register UI objects
|
|
for uiObj in UI_OBJECTS:
|
|
catalogService.registerUiObject(
|
|
featureCode=FEATURE_CODE,
|
|
objectKey=uiObj["objectKey"],
|
|
label=uiObj["label"],
|
|
meta=uiObj.get("meta")
|
|
)
|
|
|
|
# Register DATA objects
|
|
for dataObj in DATA_OBJECTS:
|
|
catalogService.registerDataObject(
|
|
featureCode=FEATURE_CODE,
|
|
objectKey=dataObj["objectKey"],
|
|
label=dataObj["label"],
|
|
meta=dataObj.get("meta")
|
|
)
|
|
|
|
# Register RESOURCE objects
|
|
for resObj in RESOURCE_OBJECTS:
|
|
catalogService.registerResourceObject(
|
|
featureCode=FEATURE_CODE,
|
|
objectKey=resObj["objectKey"],
|
|
label=resObj["label"],
|
|
meta=resObj.get("meta")
|
|
)
|
|
|
|
# Register feature definition
|
|
catalogService.registerFeatureDefinition(
|
|
featureCode=FEATURE_CODE,
|
|
label=FEATURE_LABEL,
|
|
icon=FEATURE_ICON
|
|
)
|
|
|
|
logger.info(f"Registered system RBAC objects: {len(UI_OBJECTS)} UI, {len(DATA_OBJECTS)} DATA, {len(RESOURCE_OBJECTS)} RESOURCE")
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.error(f"Failed to register system RBAC objects: {e}")
|
|
return False
|