From 237d9b5752d0e1e583bdd1dc7994ff7572c754d8 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Mon, 1 Sep 2025 23:37:11 +0200
Subject: [PATCH] ready for user test 1.00
---
modules/interfaces/interfaceAppModel.py | 23 +++++-
modules/routes/routeDataConnections.py | 102 +++++++++++++++++++++++-
2 files changed, 120 insertions(+), 5 deletions(-)
diff --git a/modules/interfaces/interfaceAppModel.py b/modules/interfaces/interfaceAppModel.py
index 6e087f63..ec95aaf5 100644
--- a/modules/interfaces/interfaceAppModel.py
+++ b/modules/interfaces/interfaceAppModel.py
@@ -156,6 +156,25 @@ class UserConnection(BaseModel, ModelMixin):
frontend_readonly=True,
frontend_required=False
)
+ tokenStatus: Optional[str] = Field(
+ None,
+ description="Current token status: active, expired, none",
+ frontend_type="select",
+ frontend_readonly=True,
+ frontend_required=False,
+ frontend_options=[
+ {"value": "active", "label": {"en": "Active", "fr": "Actif"}},
+ {"value": "expired", "label": {"en": "Expired", "fr": "Expiré"}},
+ {"value": "none", "label": {"en": "None", "fr": "Aucun"}}
+ ]
+ )
+ tokenExpiresAt: Optional[float] = Field(
+ None,
+ description="When the current token expires (UTC timestamp in seconds)",
+ frontend_type="timestamp",
+ frontend_readonly=True,
+ frontend_required=False
+ )
# Register labels for UserConnection
register_model_labels(
@@ -171,7 +190,9 @@ register_model_labels(
"status": {"en": "Status", "fr": "Statut"},
"connectedAt": {"en": "Connected At", "fr": "Connecté le"},
"lastChecked": {"en": "Last Checked", "fr": "Dernière vérification"},
- "expiresAt": {"en": "Expires At", "fr": "Expire le"}
+ "expiresAt": {"en": "Expires At", "fr": "Expire le"},
+ "tokenStatus": {"en": "Connection Status", "fr": "Statut de connexion"},
+ "tokenExpiresAt": {"en": "Expires At", "fr": "Expire le"}
}
)
diff --git a/modules/routes/routeDataConnections.py b/modules/routes/routeDataConnections.py
index c108480e..cf861e85 100644
--- a/modules/routes/routeDataConnections.py
+++ b/modules/routes/routeDataConnections.py
@@ -15,7 +15,7 @@ from datetime import datetime
import logging
import json
-from modules.interfaces.interfaceAppModel import User, UserConnection, AuthAuthority, ConnectionStatus
+from modules.interfaces.interfaceAppModel import User, UserConnection, AuthAuthority, ConnectionStatus, Token
from modules.security.auth import getCurrentUser, limiter
from modules.interfaces.interfaceAppObjects import getInterface, getRootInterface
from modules.shared.timezoneUtils import get_utc_timestamp
@@ -23,6 +23,57 @@ from modules.shared.timezoneUtils import get_utc_timestamp
# Configure logger
logger = logging.getLogger(__name__)
+def get_token_status_for_connection(interface, connection_id: str) -> tuple[str, Optional[float]]:
+ """
+ Get token status and expiration for a connection.
+
+ Args:
+ interface: The database interface
+ connection_id: The connection ID to check
+
+ Returns:
+ tuple: (token_status, token_expires_at)
+ - token_status: 'active', 'expired', or 'none'
+ - token_expires_at: UTC timestamp or None
+ """
+ try:
+ # Query tokens table for the latest token for this connection
+ tokens = interface.db.getRecordset(
+ table="tokens",
+ recordFilter={"connectionId": connection_id}
+ )
+
+ if not tokens:
+ return "none", None
+
+ # Find the most recent token (highest createdAt timestamp)
+ latest_token = None
+ latest_created_at = 0
+
+ for token_data in tokens:
+ created_at = token_data.get("createdAt", 0)
+ if created_at > latest_created_at:
+ latest_created_at = created_at
+ latest_token = token_data
+
+ if not latest_token:
+ return "none", None
+
+ # Check if token is expired
+ expires_at = latest_token.get("expiresAt")
+ if not expires_at:
+ return "none", None
+
+ current_time = get_utc_timestamp()
+ if expires_at <= current_time:
+ return "expired", expires_at
+ else:
+ return "active", expires_at
+
+ except Exception as e:
+ logger.error(f"Error getting token status for connection {connection_id}: {str(e)}")
+ return "none", None
+
router = APIRouter(
prefix="/api/connections",
tags=["Manage Connections"],
@@ -47,7 +98,32 @@ async def get_connections(
# 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)
+ connections = interface.getUserConnections(currentUser.id)
+
+ # Enhance each connection with token status information
+ enhanced_connections = []
+ for connection in connections:
+ # Get token status for this connection
+ token_status, token_expires_at = get_token_status_for_connection(interface, connection.id)
+
+ # Create enhanced connection with token status
+ enhanced_connection = UserConnection(
+ id=connection.id,
+ userId=connection.userId,
+ authority=connection.authority,
+ externalId=connection.externalId,
+ externalUsername=connection.externalUsername,
+ externalEmail=connection.externalEmail,
+ status=connection.status,
+ connectedAt=connection.connectedAt,
+ lastChecked=connection.lastChecked,
+ expiresAt=connection.expiresAt,
+ tokenStatus=token_status,
+ tokenExpiresAt=token_expires_at
+ )
+ enhanced_connections.append(enhanced_connection)
+
+ return enhanced_connections
except Exception as e:
logger.error(f"Error getting connections: {str(e)}")
@@ -164,8 +240,26 @@ async def update_connection(
# Clear cache to ensure fresh data
interface.db.clearTableCache("connections")
- # Get updated connection
- return connection
+ # Get token status for the updated connection
+ token_status, token_expires_at = get_token_status_for_connection(interface, connectionId)
+
+ # Create enhanced connection with token status
+ enhanced_connection = UserConnection(
+ id=connection.id,
+ userId=connection.userId,
+ authority=connection.authority,
+ externalId=connection.externalId,
+ externalUsername=connection.externalUsername,
+ externalEmail=connection.externalEmail,
+ status=connection.status,
+ connectedAt=connection.connectedAt,
+ lastChecked=connection.lastChecked,
+ expiresAt=connection.expiresAt,
+ tokenStatus=token_status,
+ tokenExpiresAt=token_expires_at
+ )
+
+ return enhanced_connection
except HTTPException:
raise