# 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 # - : 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