fixed connections access

This commit is contained in:
ValueOn AG 2025-08-23 01:47:50 +02:00
parent 33adccf87d
commit 1b59810e0d
4 changed files with 91 additions and 84 deletions

View file

@ -89,6 +89,11 @@ async def createActionDefinitionPrompt(context, service) -> str:
docRefs = service.getDocumentReferenceList() docRefs = service.getDocumentReferenceList()
connRefs = service.getConnectionReferenceList() 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 # Get documents from current round (chat) and entire workflow history
current_round_docs = docRefs.get('chat', []) current_round_docs = docRefs.get('chat', [])
workflow_history_docs = docRefs.get('history', []) workflow_history_docs = docRefs.get('history', [])

View file

@ -443,12 +443,23 @@ class ServiceCenter:
"""Get list of all UserConnection objects as references with enhanced state information""" """Get list of all UserConnection objects as references with enhanced state information"""
connections = [] connections = []
# Get user connections through AppObjects interface # 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) 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: for conn in user_connections:
# Get enhanced connection reference with state information # Get enhanced connection reference with state information
enhanced_ref = self.getConnectionReferenceFromUserConnection(conn) enhanced_ref = self.getConnectionReferenceFromUserConnection(conn)
logger.debug(f"getConnectionReferenceList: Enhanced ref for connection {conn.id}: {enhanced_ref}")
connections.append(enhanced_ref) connections.append(enhanced_ref)
# Sort by connection reference # Sort by connection reference
logger.debug(f"getConnectionReferenceList: Final connections list: {connections}")
return sorted(connections) return sorted(connections)
def getConnectionReferenceFromUserConnection(self, connection: UserConnection) -> str: def getConnectionReferenceFromUserConnection(self, connection: UserConnection) -> str:
@ -461,6 +472,8 @@ class ServiceCenter:
if token: if token:
if hasattr(token, 'expiresAt') and token.expiresAt: if hasattr(token, 'expiresAt') and token.expiresAt:
current_time = get_utc_timestamp() 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: if current_time > token.expiresAt:
token_status = "expired" token_status = "expired"
else: else:
@ -476,6 +489,7 @@ class ServiceCenter:
base_ref = f"connection:{connection.authority.value}:{connection.externalUsername}:{connection.id}" base_ref = f"connection:{connection.authority.value}:{connection.externalUsername}:{connection.id}"
state_info = f" [status:{connection.status.value}, token:{token_status}]" 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 return base_ref + state_info
def getUserConnectionFromConnectionReference(self, connectionReference: str) -> Optional[UserConnection]: def getUserConnectionFromConnectionReference(self, connectionReference: str) -> Optional[UserConnection]:

View file

@ -277,13 +277,21 @@ class AppObjects:
def getUserConnections(self, userId: str) -> List[UserConnection]: def getUserConnections(self, userId: str) -> List[UserConnection]:
"""Returns all connections for a user.""" """Returns all connections for a user."""
try: 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 # Get connections for this user
connections = self.db.getRecordset("connections", recordFilter={"userId": userId}) 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 # Convert to UserConnection objects
result = [] result = []
for conn_dict in connections: for conn_dict in connections:
try: try:
logger.debug(f"getUserConnections: Processing connection dict: {conn_dict}")
# Create UserConnection object # Create UserConnection object
connection = UserConnection( connection = UserConnection(
id=conn_dict["id"], id=conn_dict["id"],
@ -297,11 +305,13 @@ class AppObjects:
lastChecked=conn_dict.get("lastChecked"), lastChecked=conn_dict.get("lastChecked"),
expiresAt=conn_dict.get("expiresAt") expiresAt=conn_dict.get("expiresAt")
) )
logger.debug(f"getUserConnections: Created UserConnection object: {connection}")
result.append(connection) result.append(connection)
except Exception as e: except Exception as e:
logger.error(f"Error converting connection dict to object: {str(e)}") logger.error(f"Error converting connection dict to object: {str(e)}")
continue continue
logger.debug(f"getUserConnections: Final result: {result}")
return result return result
except Exception as e: except Exception as e:

View file

@ -1,6 +1,11 @@
""" """
Connection routes for the backend API. Connection routes for the backend API.
Implements the endpoints for connection management. 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 from fastapi import APIRouter, HTTPException, Depends, Body, Path, Request, Response
@ -30,23 +35,20 @@ async def get_connections(
request: Request, request: Request,
currentUser: User = Depends(getCurrentUser) currentUser: User = Depends(getCurrentUser)
) -> List[UserConnection]: ) -> 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: try:
interface = getInterface(currentUser) interface = getInterface(currentUser)
# Clear connections cache to ensure fresh data # Clear connections cache to ensure fresh data
interface.db.clearTableCache("connections") interface.db.clearTableCache("connections")
if currentUser.privilege in ['admin', 'sysadmin']: # SECURITY FIX: All users (including admins) can only see their own connections
# Admins can see all connections # This prevents admin from seeing other users' connections and causing confusion
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) return interface.getUserConnections(currentUser.id)
except Exception as e: except Exception as e:
logger.error(f"Error getting connections: {str(e)}") logger.error(f"Error getting connections: {str(e)}")
raise HTTPException( raise HTTPException(
@ -61,6 +63,11 @@ async def create_connection(
connection_data: Dict[str, Any] = Body(...), connection_data: Dict[str, Any] = Body(...),
currentUser: User = Depends(getCurrentUser) currentUser: User = Depends(getCurrentUser)
) -> UserConnection: ) -> 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: try:
interface = getInterface(currentUser) interface = getInterface(currentUser)
@ -120,25 +127,17 @@ async def update_connection(
connection_data: Dict[str, Any] = Body(...), connection_data: Dict[str, Any] = Body(...),
currentUser: User = Depends(getCurrentUser) currentUser: User = Depends(getCurrentUser)
) -> UserConnection: ) -> 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: try:
interface = getInterface(currentUser) interface = getInterface(currentUser)
# Find the connection # Find the connection
connection = None connection = None
if currentUser.privilege in ['admin', 'sysadmin']: # SECURITY FIX: All users (including admins) can only update their own connections
# Admins can update any connection # This prevents admin from updating other users' connections and causing confusion
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) connections = interface.getUserConnections(currentUser.id)
for conn in connections: for conn in connections:
if conn.id == connectionId: if conn.id == connectionId:
@ -184,25 +183,18 @@ async def connect_service(
connectionId: str = Path(..., description="The ID of the connection to connect"), connectionId: str = Path(..., description="The ID of the connection to connect"),
currentUser: User = Depends(getCurrentUser) currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Connect a service for the current user
SECURITY: This endpoint is secure - users can only connect their own connections.
"""
try: try:
interface = getInterface(currentUser) interface = getInterface(currentUser)
# Find the connection # Find the connection
connection = None connection = None
if currentUser.privilege in ['admin', 'sysadmin']: # SECURITY FIX: All users (including admins) can only connect their own connections
# Admins can connect any connection # This prevents admin from connecting other users' connections and causing confusion
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) connections = interface.getUserConnections(currentUser.id)
for conn in connections: for conn in connections:
if conn.id == connectionId: if conn.id == connectionId:
@ -257,25 +249,18 @@ async def disconnect_service(
connectionId: str = Path(..., description="The ID of the connection to disconnect"), connectionId: str = Path(..., description="The ID of the connection to disconnect"),
currentUser: User = Depends(getCurrentUser) currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Disconnect a service for the current user
SECURITY: This endpoint is secure - users can only disconnect their own connections.
"""
try: try:
interface = getInterface(currentUser) interface = getInterface(currentUser)
# Find the connection # Find the connection
connection = None connection = None
if currentUser.privilege in ['admin', 'sysadmin']: # SECURITY FIX: All users (including admins) can only disconnect their own connections
# Admins can disconnect any connection # This prevents admin from disconnecting other users' connections and causing confusion
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) connections = interface.getUserConnections(currentUser.id)
for conn in connections: for conn in connections:
if conn.id == connectionId: if conn.id == connectionId:
@ -316,25 +301,18 @@ async def delete_connection(
connectionId: str = Path(..., description="The ID of the connection to delete"), connectionId: str = Path(..., description="The ID of the connection to delete"),
currentUser: User = Depends(getCurrentUser) currentUser: User = Depends(getCurrentUser)
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Delete a connection for the current user
SECURITY: This endpoint is secure - users can only delete their own connections.
"""
try: try:
interface = getInterface(currentUser) interface = getInterface(currentUser)
# Find the connection # Find the connection
connection = None connection = None
if currentUser.privilege in ['admin', 'sysadmin']: # SECURITY FIX: All users (including admins) can only delete their own connections
# Admins can delete any connection # This prevents admin from deleting other users' connections and causing confusion
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) connections = interface.getUserConnections(currentUser.id)
for conn in connections: for conn in connections:
if conn.id == connectionId: if conn.id == connectionId: