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()
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', [])

View file

@ -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]:

View file

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

View file

@ -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(