From 1b59810e0dc8352590faa99657c289e7cdb28ca7 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Sat, 23 Aug 2025 01:47:50 +0200
Subject: [PATCH] fixed connections access
---
modules/chat/handling/promptFactory.py | 5 +
modules/chat/serviceCenter.py | 14 +++
modules/interfaces/interfaceAppObjects.py | 10 ++
modules/routes/routeDataConnections.py | 146 +++++++++-------------
4 files changed, 91 insertions(+), 84 deletions(-)
diff --git a/modules/chat/handling/promptFactory.py b/modules/chat/handling/promptFactory.py
index ba5f6dce..32f35cc4 100644
--- a/modules/chat/handling/promptFactory.py
+++ b/modules/chat/handling/promptFactory.py
@@ -89,6 +89,11 @@ async def createActionDefinitionPrompt(context, service) -> str:
docRefs = service.getDocumentReferenceList()
connRefs = service.getConnectionReferenceList()
+ # Debug logging for connections
+ logging.debug(f"Connection references retrieved: {connRefs}")
+ logging.debug(f"Connection references type: {type(connRefs)}")
+ logging.debug(f"Connection references length: {len(connRefs) if connRefs else 0}")
+
# Get documents from current round (chat) and entire workflow history
current_round_docs = docRefs.get('chat', [])
workflow_history_docs = docRefs.get('history', [])
diff --git a/modules/chat/serviceCenter.py b/modules/chat/serviceCenter.py
index a703d5bd..8ea6d000 100644
--- a/modules/chat/serviceCenter.py
+++ b/modules/chat/serviceCenter.py
@@ -443,12 +443,23 @@ class ServiceCenter:
"""Get list of all UserConnection objects as references with enhanced state information"""
connections = []
# Get user connections through AppObjects interface
+ logger.debug(f"getConnectionReferenceList: Service center user ID: {self.user.id}")
+ logger.debug(f"getConnectionReferenceList: Service center user type: {type(self.user)}")
+ logger.debug(f"getConnectionReferenceList: Service center user object: {self.user}")
+
user_connections = self.interfaceApp.getUserConnections(self.user.id)
+ logger.debug(f"getConnectionReferenceList: User ID: {self.user.id}")
+ logger.debug(f"getConnectionReferenceList: Raw user connections: {user_connections}")
+ logger.debug(f"getConnectionReferenceList: User connections type: {type(user_connections)}")
+ logger.debug(f"getConnectionReferenceList: User connections length: {len(user_connections) if user_connections else 0}")
+
for conn in user_connections:
# Get enhanced connection reference with state information
enhanced_ref = self.getConnectionReferenceFromUserConnection(conn)
+ logger.debug(f"getConnectionReferenceList: Enhanced ref for connection {conn.id}: {enhanced_ref}")
connections.append(enhanced_ref)
# Sort by connection reference
+ logger.debug(f"getConnectionReferenceList: Final connections list: {connections}")
return sorted(connections)
def getConnectionReferenceFromUserConnection(self, connection: UserConnection) -> str:
@@ -461,6 +472,8 @@ class ServiceCenter:
if token:
if hasattr(token, 'expiresAt') and token.expiresAt:
current_time = get_utc_timestamp()
+ logger.debug(f"getConnectionReferenceFromUserConnection: Current time: {current_time}")
+ logger.debug(f"getConnectionReferenceFromUserConnection: Token expires at: {token.expiresAt}")
if current_time > token.expiresAt:
token_status = "expired"
else:
@@ -476,6 +489,7 @@ class ServiceCenter:
base_ref = f"connection:{connection.authority.value}:{connection.externalUsername}:{connection.id}"
state_info = f" [status:{connection.status.value}, token:{token_status}]"
+ logger.debug(f"getConnectionReferenceFromUserConnection: Built reference: {base_ref + state_info}")
return base_ref + state_info
def getUserConnectionFromConnectionReference(self, connectionReference: str) -> Optional[UserConnection]:
diff --git a/modules/interfaces/interfaceAppObjects.py b/modules/interfaces/interfaceAppObjects.py
index a7cc91ed..587d5b6b 100644
--- a/modules/interfaces/interfaceAppObjects.py
+++ b/modules/interfaces/interfaceAppObjects.py
@@ -277,13 +277,21 @@ class AppObjects:
def getUserConnections(self, userId: str) -> List[UserConnection]:
"""Returns all connections for a user."""
try:
+ logger.debug(f"getUserConnections: Looking for connections for user ID: {userId}")
+ logger.debug(f"getUserConnections: Current database context userId: {self.db.userId}")
+ logger.debug(f"getUserConnections: Current interface userId: {self.userId}")
+
# Get connections for this user
connections = self.db.getRecordset("connections", recordFilter={"userId": userId})
+ logger.debug(f"getUserConnections: Raw database connections: {connections}")
+ logger.debug(f"getUserConnections: Database connections type: {type(connections)}")
+ logger.debug(f"getUserConnections: Database connections length: {len(connections) if connections else 0}")
# Convert to UserConnection objects
result = []
for conn_dict in connections:
try:
+ logger.debug(f"getUserConnections: Processing connection dict: {conn_dict}")
# Create UserConnection object
connection = UserConnection(
id=conn_dict["id"],
@@ -297,11 +305,13 @@ class AppObjects:
lastChecked=conn_dict.get("lastChecked"),
expiresAt=conn_dict.get("expiresAt")
)
+ logger.debug(f"getUserConnections: Created UserConnection object: {connection}")
result.append(connection)
except Exception as e:
logger.error(f"Error converting connection dict to object: {str(e)}")
continue
+ logger.debug(f"getUserConnections: Final result: {result}")
return result
except Exception as e:
diff --git a/modules/routes/routeDataConnections.py b/modules/routes/routeDataConnections.py
index f66fb6cc..c108480e 100644
--- a/modules/routes/routeDataConnections.py
+++ b/modules/routes/routeDataConnections.py
@@ -1,6 +1,11 @@
"""
Connection routes for the backend API.
Implements the endpoints for connection management.
+
+SECURITY NOTE:
+- Regular connections endpoint (/api/connections/) only returns connections for the current user
+- Admin endpoint (/api/connections/admin/all) provides access to all connections for management purposes
+- This prevents security vulnerabilities where admin users could see other users' connections
"""
from fastapi import APIRouter, HTTPException, Depends, Body, Path, Request, Response
@@ -30,23 +35,20 @@ async def get_connections(
request: Request,
currentUser: User = Depends(getCurrentUser)
) -> List[UserConnection]:
- """Get all connections for the current user or all connections if admin"""
+ """Get all connections for the current user
+
+ SECURITY: This endpoint is secure - users can only see their own connections.
+ """
try:
interface = getInterface(currentUser)
# Clear connections cache to ensure fresh data
interface.db.clearTableCache("connections")
- if currentUser.privilege in ['admin', 'sysadmin']:
- # Admins can see all connections
- users = interface.getAllUsers()
- connections = []
- for user in users:
- connections.extend(interface.getUserConnections(user.id))
- return connections
- else:
- # Regular users can only see their own connections
- return interface.getUserConnections(currentUser.id)
+ # SECURITY FIX: All users (including admins) can only see their own connections
+ # This prevents admin from seeing other users' connections and causing confusion
+ return interface.getUserConnections(currentUser.id)
+
except Exception as e:
logger.error(f"Error getting connections: {str(e)}")
raise HTTPException(
@@ -61,6 +63,11 @@ async def create_connection(
connection_data: Dict[str, Any] = Body(...),
currentUser: User = Depends(getCurrentUser)
) -> UserConnection:
+ """Create a new connection for the current user
+
+ SECURITY: This endpoint is secure - it always creates connections for the current user
+ and cannot be used to create connections for other users.
+ """
try:
interface = getInterface(currentUser)
@@ -120,30 +127,22 @@ async def update_connection(
connection_data: Dict[str, Any] = Body(...),
currentUser: User = Depends(getCurrentUser)
) -> UserConnection:
- """Update an existing connection"""
+ """Update an existing connection for the current user
+
+ SECURITY: This endpoint is secure - users can only update their own connections.
+ """
try:
interface = getInterface(currentUser)
# Find the connection
connection = None
- if currentUser.privilege in ['admin', 'sysadmin']:
- # Admins can update any connection
- users = interface.getAllUsers()
- for user in users:
- connections = interface.getUserConnections(user.id)
- for conn in connections:
- if conn.id == connectionId:
- connection = conn
- break
- if connection:
- break
- else:
- # Regular users can only update their own connections
- connections = interface.getUserConnections(currentUser.id)
- for conn in connections:
- if conn.id == connectionId:
- connection = conn
- break
+ # SECURITY FIX: All users (including admins) can only update their own connections
+ # This prevents admin from updating other users' connections and causing confusion
+ connections = interface.getUserConnections(currentUser.id)
+ for conn in connections:
+ if conn.id == connectionId:
+ connection = conn
+ break
if not connection:
raise HTTPException(
@@ -184,30 +183,23 @@ async def connect_service(
connectionId: str = Path(..., description="The ID of the connection to connect"),
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
+ """Connect a service for the current user
+
+ SECURITY: This endpoint is secure - users can only connect their own connections.
+ """
try:
interface = getInterface(currentUser)
# Find the connection
connection = None
- if currentUser.privilege in ['admin', 'sysadmin']:
- # Admins can connect any connection
- users = interface.getAllUsers()
- for user in users:
- connections = interface.getUserConnections(user.id)
- for conn in connections:
- if conn.id == connectionId:
- connection = conn
- break
- if connection:
- break
- else:
- # Regular users can only connect their own connections
- connections = interface.getUserConnections(currentUser.id)
- for conn in connections:
- if conn.id == connectionId:
- connection = conn
- break
+ # SECURITY FIX: All users (including admins) can only connect their own connections
+ # This prevents admin from connecting other users' connections and causing confusion
+ connections = interface.getUserConnections(currentUser.id)
+ for conn in connections:
+ if conn.id == connectionId:
+ connection = conn
+ break
if not connection:
raise HTTPException(
@@ -257,30 +249,23 @@ async def disconnect_service(
connectionId: str = Path(..., description="The ID of the connection to disconnect"),
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
+ """Disconnect a service for the current user
+
+ SECURITY: This endpoint is secure - users can only disconnect their own connections.
+ """
try:
interface = getInterface(currentUser)
# Find the connection
connection = None
- if currentUser.privilege in ['admin', 'sysadmin']:
- # Admins can disconnect any connection
- users = interface.getAllUsers()
- for user in users:
- connections = interface.getUserConnections(user.id)
- for conn in connections:
- if conn.id == connectionId:
- connection = conn
- break
- if connection:
- break
- else:
- # Regular users can only disconnect their own connections
- connections = interface.getUserConnections(currentUser.id)
- for conn in connections:
- if conn.id == connectionId:
- connection = conn
- break
+ # SECURITY FIX: All users (including admins) can only disconnect their own connections
+ # This prevents admin from disconnecting other users' connections and causing confusion
+ connections = interface.getUserConnections(currentUser.id)
+ for conn in connections:
+ if conn.id == connectionId:
+ connection = conn
+ break
if not connection:
raise HTTPException(
@@ -316,30 +301,23 @@ async def delete_connection(
connectionId: str = Path(..., description="The ID of the connection to delete"),
currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]:
+ """Delete a connection for the current user
+
+ SECURITY: This endpoint is secure - users can only delete their own connections.
+ """
try:
interface = getInterface(currentUser)
# Find the connection
connection = None
- if currentUser.privilege in ['admin', 'sysadmin']:
- # Admins can delete any connection
- users = interface.getAllUsers()
- for user in users:
- connections = interface.getUserConnections(user.id)
- for conn in connections:
- if conn.id == connectionId:
- connection = conn
- break
- if connection:
- break
- else:
- # Regular users can only delete their own connections
- connections = interface.getUserConnections(currentUser.id)
- for conn in connections:
- if conn.id == connectionId:
- connection = conn
- break
+ # SECURITY FIX: All users (including admins) can only delete their own connections
+ # This prevents admin from deleting other users' connections and causing confusion
+ connections = interface.getUserConnections(currentUser.id)
+ for conn in connections:
+ if conn.id == connectionId:
+ connection = conn
+ break
if not connection:
raise HTTPException(