From 468bdb90001f41c0f6ccb3dfdc7474da4462dfd3 Mon Sep 17 00:00:00 2001 From: busya Date: Mon, 6 Jul 2026 12:13:26 +0300 Subject: [PATCH] =?UTF-8?q?fix(agent):=20resolve=20missing=20imports=20?= =?UTF-8?q?=E2=80=94=20jose,=20auth=20config,=20AUTH=5FSECRET=5FKEY?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added python-jose[cryptography] to requirements-agent.txt - Made sqlalchemy.orm.Session and TokenBlacklist imports lazy in jwt.py so decode_token works without pulling ORM models into agent image - Added src/core/auth/__init__.py, config.py, jwt.py to agent Dockerfile COPY - Added AUTH_SECRET_KEY env var to agent compose (reads from JWT_SECRET) --- backend/requirements-agent.txt | 4 +++ backend/src/core/auth/jwt.py | 41 +++++++++++++++++------------ build.sh | 2 ++ docker-compose.enterprise-clean.yml | 1 + docker-compose.yml | 1 + docker/Dockerfile.agent | 3 +++ 6 files changed, 35 insertions(+), 17 deletions(-) diff --git a/backend/requirements-agent.txt b/backend/requirements-agent.txt index 4e183195..bddbebaf 100644 --- a/backend/requirements-agent.txt +++ b/backend/requirements-agent.txt @@ -37,3 +37,7 @@ psycopg>=3.1 # Retry/utility tenacity>=8.0.0 requests>=2.32.0 + +# JWT token validation (used by src.core.auth.jwt.decode_token) +python-jose[cryptography] + diff --git a/backend/src/core/auth/jwt.py b/backend/src/core/auth/jwt.py index 6ade2d3c..4666cb0e 100644 --- a/backend/src/core/auth/jwt.py +++ b/backend/src/core/auth/jwt.py @@ -16,9 +16,7 @@ import hashlib import uuid from jose import jwt -from sqlalchemy.orm import Session -from ...models.auth import TokenBlacklist from ..logger import belief_scope from .config import auth_config @@ -38,20 +36,18 @@ def create_access_token(data: dict, expires_delta: timedelta | None = None) -> s if expires_delta: expire = now + expires_delta else: - expire = now + timedelta( - minutes=auth_config.ACCESS_TOKEN_EXPIRE_MINUTES - ) + expire = now + timedelta(minutes=auth_config.ACCESS_TOKEN_EXPIRE_MINUTES) - to_encode.update({ - "exp": expire, - "iat": now, - "jti": str(uuid.uuid4()), - "aud": auth_config.JWT_AUDIENCE, - "iss": auth_config.JWT_ISSUER, - }) - encoded_jwt = jwt.encode( - to_encode, auth_config.SECRET_KEY, algorithm=auth_config.ALGORITHM + to_encode.update( + { + "exp": expire, + "iat": now, + "jti": str(uuid.uuid4()), + "aud": auth_config.JWT_AUDIENCE, + "iss": auth_config.JWT_ISSUER, + } ) + encoded_jwt = jwt.encode(to_encode, auth_config.SECRET_KEY, algorithm=auth_config.ALGORITHM) return encoded_jwt @@ -100,6 +96,7 @@ def decode_token(token: str) -> dict: def _hash_token(token: str) -> str: return hashlib.sha256(token.encode()).hexdigest() + # #endregion Auth.Jwt._HashToken @@ -108,11 +105,15 @@ def _hash_token(token: str) -> str: # @PRE db is a valid SQLAlchemy session. # @POST Expired blacklist rows are removed. # @SIDE_EFFECT Mutates token_blacklist table. -def _prune_blacklist(db: Session) -> None: +def _prune_blacklist(db: "Session") -> None: + from sqlalchemy.orm import Session # noqa: F811 + from ...models.auth import TokenBlacklist + now = datetime.now(UTC) db.query(TokenBlacklist).filter(TokenBlacklist.expires_at < now).delete() db.commit() + # #endregion Auth.Jwt._PruneBlacklist @@ -125,7 +126,10 @@ def _prune_blacklist(db: Session) -> None: # @RELATION DEPENDS_ON -> [TokenBlacklist] # @RELATION CALLS -> [Auth.Jwt._HashToken] # @TEST_EDGE: already_expired -> skips blacklisting -def blacklist_token(token: str, db: Session) -> None: +def blacklist_token(token: str, db: "Session") -> None: + from sqlalchemy.orm import Session # noqa: F811 + from ...models.auth import TokenBlacklist + with belief_scope("blacklist_token"): _prune_blacklist(db) try: @@ -165,7 +169,10 @@ def blacklist_token(token: str, db: Session) -> None: # @PRE db is a valid SQLAlchemy session. # @POST Returns True if token is blacklisted, False otherwise. # @RELATION DEPENDS_ON -> [TokenBlacklist] -def is_token_blacklisted(token: str, db: Session) -> bool: +def is_token_blacklisted(token: str, db: "Session") -> bool: + from sqlalchemy.orm import Session # noqa: F811 + from ...models.auth import TokenBlacklist + with belief_scope("is_token_blacklisted"): try: payload = decode_token(token) diff --git a/build.sh b/build.sh index 73b8797d..a1fb6516 100755 --- a/build.sh +++ b/build.sh @@ -307,6 +307,7 @@ services: LLM_MODEL: \${LLM_MODEL:-gpt-4o} FASTAPI_URL: http://backend:8000 JWT_SECRET: \${JWT_SECRET:-super-secret-key} + AUTH_SECRET_KEY: \${JWT_SECRET:-super-secret-key} SERVICE_JWT: \${SERVICE_JWT:-agent-service-secret} DATABASE_URL: postgresql+psycopg2://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}@\${POSTGRES_HOST:?Set POSTGRES_HOST}:\${POSTGRES_PORT:-5432}/\${POSTGRES_DB:-ss_tools} GRADIO_SERVER_PORT: 7860 @@ -611,6 +612,7 @@ services: LLM_MODEL: \${LLM_MODEL:-gpt-4o} FASTAPI_URL: http://backend:8000 JWT_SECRET: \${JWT_SECRET:-super-secret-key} + AUTH_SECRET_KEY: \${JWT_SECRET:-super-secret-key} SERVICE_JWT: \${SERVICE_JWT:-agent-service-secret} DATABASE_URL: postgresql+psycopg2://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}@\${POSTGRES_HOST:?Set POSTGRES_HOST}:\${POSTGRES_PORT:-5432}/\${POSTGRES_DB:-ss_tools} GRADIO_SERVER_PORT: 7860 diff --git a/docker-compose.enterprise-clean.yml b/docker-compose.enterprise-clean.yml index 51186960..e3865aa5 100644 --- a/docker-compose.enterprise-clean.yml +++ b/docker-compose.enterprise-clean.yml @@ -104,6 +104,7 @@ services: LLM_MODEL: ${LLM_MODEL:-gpt-4o} FASTAPI_URL: http://backend:8000 JWT_SECRET: ${JWT_SECRET:?JWT_SECRET must be set — crash-early, no default fallback} + AUTH_SECRET_KEY: ${JWT_SECRET:?JWT_SECRET must be set — crash-early, no default fallback} SERVICE_JWT: ${SERVICE_JWT:-agent-service-secret} DATABASE_URL: postgresql+psycopg2://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD in .env.enterprise-clean}@${POSTGRES_HOST:?Set POSTGRES_HOST in .env.enterprise-clean}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-ss_tools} GRADIO_SERVER_PORT: 7860 diff --git a/docker-compose.yml b/docker-compose.yml index 71bcf6a3..09212ecc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -60,6 +60,7 @@ services: LLM_MODEL: ${LLM_MODEL:-gpt-4o} FASTAPI_URL: http://backend:8000 JWT_SECRET: ${JWT_SECRET:?JWT_SECRET must be set — crash-early, no default fallback} + AUTH_SECRET_KEY: ${JWT_SECRET:?JWT_SECRET must be set — crash-early, no default fallback} SERVICE_JWT: ${SERVICE_JWT:-agent-service-secret} DATABASE_URL: postgresql+psycopg2://postgres:postgres@db:5432/ss_tools GRADIO_SERVER_PORT: 7860 diff --git a/docker/Dockerfile.agent b/docker/Dockerfile.agent index 1e9403e4..27a86ddc 100644 --- a/docker/Dockerfile.agent +++ b/docker/Dockerfile.agent @@ -45,6 +45,9 @@ COPY backend/src/core/__init__.py /app/backend/src/core/__init__.py COPY backend/src/core/cot_logger.py /app/backend/src/core/cot_logger.py COPY backend/src/core/logger.py /app/backend/src/core/logger.py COPY backend/src/core/ws_log_handler.py /app/backend/src/core/ws_log_handler.py +COPY backend/src/core/auth/__init__.py /app/backend/src/core/auth/__init__.py +COPY backend/src/core/auth/config.py /app/backend/src/core/auth/config.py +COPY backend/src/core/auth/jwt.py /app/backend/src/core/auth/jwt.py COPY backend/src/agent/ /app/backend/src/agent/ # Gradio server