From 26fc6aa0ba7e8b7bb9dd2a75ef306f840610b120 Mon Sep 17 00:00:00 2001 From: benflexcompute Date: Fri, 24 Apr 2026 13:19:55 -0400 Subject: [PATCH] feat(s3): support credential-scoped endpoint and OSS storage provider Add `endpoint` and `storageProvider` fields to `_UserCredential` so the STS response can point the S3 client at alternative backends. When `storageProvider == OSS`, disable AWS checksum headers and switch to virtual-hosted addressing. Co-Authored-By: Claude Opus 4.7 (1M context) --- flow360/cloud/s3_utils.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/flow360/cloud/s3_utils.py b/flow360/cloud/s3_utils.py index df46feb84..4d97f7804 100644 --- a/flow360/cloud/s3_utils.py +++ b/flow360/cloud/s3_utils.py @@ -8,6 +8,7 @@ from abc import ABCMeta, abstractmethod from datetime import datetime from enum import Enum +from typing import Optional # pylint: disable=unused-import from pydantic.v1 import BaseModel, Field @@ -115,6 +116,8 @@ class _UserCredential(BaseModel): secret_access_key: str = Field(alias="secretAccessKey") session_token: str = Field(alias="sessionToken") region: str + endpoint: Optional[str] = None + storage_provider: Optional[str] = Field(alias="storageProvider", default=None) class _S3STSToken(BaseModel): @@ -149,7 +152,13 @@ def get_client(self): # pylint: disable=no-member config_kwargs = {"max_pool_connections": MAX_POOL} - if Env.current.s3_endpoint_url is not None: + if (self.user_credential.storage_provider or "").upper() == "OSS": + # OSS does not support aws integrity check + config_kwargs["request_checksum_calculation"] = "when_required" + config_kwargs["response_checksum_validation"] = "when_required" + # OSS recommends virtual-hosted style addressing (http://bucket.host/key). + config_kwargs["s3"] = {"addressing_style": "virtual"} + elif Env.current.s3_endpoint_url is not None: # S3-compatible stores (s3proxy, MinIO) may not implement the # checksum headers that newer boto3 versions send by default. config_kwargs["request_checksum_calculation"] = "when_required" @@ -175,6 +184,9 @@ def get_client(self): "config": config, } + if self.user_credential.endpoint is not None: + kwargs["endpoint_url"] = self.user_credential.endpoint + if Env.current.s3_endpoint_url is not None: kwargs["endpoint_url"] = Env.current.s3_endpoint_url