diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml index 0c531de9..9d859676 100644 --- a/.github/workflows/ci-test.yml +++ b/.github/workflows/ci-test.yml @@ -54,6 +54,7 @@ jobs: GOPATH=$GITHUB_WORKSPACE go get github.com/davecgh/go-spew/spew GOPATH=$GITHUB_WORKSPACE go get github.com/pmezard/go-difflib/difflib GOPATH=$GITHUB_WORKSPACE go get gopkg.in/yaml.v3 + rm -rf $GITHUB_WORKSPACE/src/github.com/BurntSushi/toml && git clone -b v0.3.1 --depth 1 https://github.com/BurntSushi/toml.git $GITHUB_WORKSPACE/src/github.com/BurntSushi/toml GOPATH=$GITHUB_WORKSPACE make unittest working-directory: src/github.com/qiniu/go-sdk diff --git a/auth/credentials.go b/auth/credentials.go index 94c3c110..639b2c18 100644 --- a/auth/credentials.go +++ b/auth/credentials.go @@ -14,6 +14,7 @@ import ( api "github.com/qiniu/go-sdk/v7" "github.com/qiniu/go-sdk/v7/conf" internal_io "github.com/qiniu/go-sdk/v7/internal/io" + "github.com/qiniu/go-sdk/v7/storagev2/defaults" ) const ( @@ -39,11 +40,17 @@ func New(accessKey, secretKey string) *Credentials { // Sign 对数据进行签名,一般用于私有空间下载用途 func (ath *Credentials) Sign(data []byte) (token string) { - h := hmac.New(sha1.New, ath.SecretKey) + var accessKey, secretKey string + if ath == nil { + accessKey, secretKey, _ = defaults.Credentials() + } else { + accessKey, secretKey = ath.AccessKey, string(ath.SecretKey) + } + h := hmac.New(sha1.New, []byte(secretKey)) h.Write(data) sign := base64.URLEncoding.EncodeToString(h.Sum(nil)) - return fmt.Sprintf("%s:%s", ath.AccessKey, sign) + return fmt.Sprintf("%s:%s", accessKey, sign) } // SignToken 根据t的类型对请求进行签名,并把token加入req中 @@ -74,8 +81,14 @@ func (ath *Credentials) SignWithData(b []byte) (token string) { // IsIAMKey 判断AccessKey是否为IAM的Key func (ath *Credentials) IsIAMKey() bool { - return len(ath.AccessKey) == IAMKeyLen*4/3 && - strings.HasPrefix(ath.AccessKey, IAMKeyPrefix) + var accessKey string + if ath == nil { + accessKey, _, _ = defaults.Credentials() + } else { + accessKey = ath.AccessKey + } + return len(accessKey) == IAMKeyLen*4/3 && + strings.HasPrefix(accessKey, IAMKeyPrefix) } func collectData(req *http.Request) (data []byte, err error) { diff --git a/cdn/api.go b/cdn/api.go index 60dd180f..19971292 100644 --- a/cdn/api.go +++ b/cdn/api.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/qiniu/go-sdk/v7/auth" + "github.com/qiniu/go-sdk/v7/storagev2/defaults" ) // Fusion CDN服务域名 @@ -24,6 +25,11 @@ type CdnManager struct { // NewCdnManager 用来构建一个新的 CdnManager func NewCdnManager(mac *auth.Credentials) *CdnManager { + if mac == nil { + if accessKey, secretKey, err := defaults.Credentials(); err == nil && accessKey != "" && secretKey != "" { + mac = auth.New(accessKey, secretKey) + } + } return &CdnManager{mac: mac} } diff --git a/conf/conf.go b/conf/conf.go index df3686b1..e8ed510f 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -1,8 +1,7 @@ package conf import ( - "os" - "strings" + "github.com/qiniu/go-sdk/v7/internal/env" ) const Version = "7.19.0" @@ -12,12 +11,9 @@ const ( CONTENT_TYPE_FORM = "application/x-www-form-urlencoded" CONTENT_TYPE_OCTET = "application/octet-stream" CONTENT_TYPE_MULTIPART = "multipart/form-data" - - disableQiniuTimestampSignatureEnvKey = "DISABLE_QINIU_TIMESTAMP_SIGNATURE" ) func IsDisableQiniuTimestampSignature() bool { - value := os.Getenv(disableQiniuTimestampSignatureEnvKey) - value = strings.ToLower(value) - return value == "true" || value == "yes" || value == "y" || value == "1" + isDisabled, _ := env.DisableQiniuTimestampSignatureFromEnvironment() + return isDisabled } diff --git a/go.mod b/go.mod index b6d1c3ea..ada4e9b9 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/qiniu/go-sdk/v7 go 1.14 require ( + github.com/BurntSushi/toml v1.3.2 github.com/dave/jennifer v1.6.1 github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect diff --git a/go.sum b/go.sum index 4ce1d40c..93a44d36 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/dave/jennifer v1.6.1 h1:T4T/67t6RAA5AIV6+NP8Uk/BIsXgDoqEowgycdQQLuk= github.com/dave/jennifer v1.6.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= diff --git a/internal/configfile/config_file.go b/internal/configfile/config_file.go new file mode 100644 index 00000000..360c9645 --- /dev/null +++ b/internal/configfile/config_file.go @@ -0,0 +1,99 @@ +package configfile + +import ( + "errors" + "os" + "path/filepath" + "strings" + + "github.com/BurntSushi/toml" + "github.com/qiniu/go-sdk/v7/internal/env" +) + +type profileConfig struct { + AccessKey string `toml:"access_key"` + SecretKey string `toml:"secret_key"` + BucketURL interface{} `toml:"bucket_url"` + DisableSecureProtocol bool `toml:"disable_secure_protocol"` +} + +var profileConfigs map[string]*profileConfig + +var ErrInvalidBucketUrl = errors.New("invalid bucket url") + +func CredentialsFromConfigFile() (string, string, error) { + profile, err := getProfile() + if err != nil || profile == nil { + return "", "", err + } else if profile.AccessKey == "" || profile.SecretKey == "" { + return "", "", nil + } + return profile.AccessKey, profile.SecretKey, nil +} + +func BucketURLsFromConfigFile() ([]string, error) { + profile, err := getProfile() + if err != nil || profile == nil { + return nil, err + } else if profile.BucketURL == "" { + return nil, nil + } + switch u := profile.BucketURL.(type) { + case string: + return strings.Split(u, ","), nil + case []interface{}: + var bucketUrls []string + for _, v := range u { + if s, ok := v.(string); ok { + bucketUrls = append(bucketUrls, s) + } else { + return nil, ErrInvalidBucketUrl + } + } + return bucketUrls, nil + } + return nil, ErrInvalidBucketUrl +} + +func DisableSecureProtocolFromConfigFile() (bool, error) { + profile, err := getProfile() + if err != nil || profile == nil { + return false, err + } + return profile.DisableSecureProtocol, nil +} + +func getProfile() (*profileConfig, error) { + if err := load(); err != nil { + return nil, err + } + profileName := env.ProfileFromEnvironment() + if profileName == "" { + profileName = "default" + } + profile, ok := profileConfigs[profileName] + if !ok || profile == nil { + return nil, nil + } + return profile, nil +} + +func load() error { + if profileConfigs != nil { + return nil + } + configFilePath := env.ConfigFileFromEnvironment() + if configFilePath == "" { + configFilePath = getDefaultConfigFilePath() + } + _, err := toml.DecodeFile(configFilePath, &profileConfigs) + return err +} + +func getDefaultConfigFilePath() string { + homeDir, err := os.UserHomeDir() + if err != nil { + homeDir = "" + } + return filepath.Join(homeDir, ".qiniu", "config.toml") +} diff --git a/internal/configfile/config_file_test.go b/internal/configfile/config_file_test.go new file mode 100644 index 00000000..7d397498 --- /dev/null +++ b/internal/configfile/config_file_test.go @@ -0,0 +1,146 @@ +//go:build unit +// +build unit + +package configfile + +import ( + "os" + "testing" +) + +func TestLoad(t *testing.T) { + file, err := os.CreateTemp("", "config.toml") + if err != nil { + t.Fatal(err) + } + defer os.Remove(file.Name()) + defer file.Close() + + _, err = file.WriteString(` +[default] +access_key = "QINIU_ACCESS_KEY_1" +secret_key = "QINIU_SECRET_KEY_1" + +[private-cloud] +access_key = "QINIU_ACCESS_KEY_2" +secret_key = "QINIU_SECRET_KEY_2" +bucket_url = "https://uc.qbox.me" +disable_secure_protocol = true + +[private-cloud-2] +access_key = "QINIU_ACCESS_KEY_3" +secret_key = "QINIU_SECRET_KEY_3" +bucket_url = ["https://uc.qbox.me", "https://uc.qiniuapi.com"] + `) + if err != nil { + t.Fatal(err) + } + + os.Setenv("QINIU_CONFIG_FILE", file.Name()) + defer os.Unsetenv("QINIU_CONFIG_FILE") + if err = load(); err != nil { + t.Fatal(err) + } + defer func() { + profileConfigs = nil + }() + if len(profileConfigs) != 3 { + t.Fatal("Unexpected profile configs") + } + if profileConfigs["default"].AccessKey != "QINIU_ACCESS_KEY_1" { + t.Fatal("Unexpected access key") + } + if profileConfigs["default"].SecretKey != "QINIU_SECRET_KEY_1" { + t.Fatal("Unexpected secret key") + } + if profileConfigs["default"].BucketURL != nil { + t.Fatal("Unexpected bucket url") + } + if profileConfigs["default"].DisableSecureProtocol { + t.Fatal("Unexpected disable secure protocol") + } + if profileConfigs["private-cloud"].AccessKey != "QINIU_ACCESS_KEY_2" { + t.Fatal("Unexpected access key") + } + if profileConfigs["private-cloud"].SecretKey != "QINIU_SECRET_KEY_2" { + t.Fatal("Unexpected secret key") + } + if profileConfigs["private-cloud"].BucketURL.(string) != "https://uc.qbox.me" { + t.Fatal("Unexpected bucket url") + } + if !profileConfigs["private-cloud"].DisableSecureProtocol { + t.Fatal("Unexpected disable secure protocol") + } + if profileConfigs["private-cloud-2"].AccessKey != "QINIU_ACCESS_KEY_3" { + t.Fatal("Unexpected access key") + } + if profileConfigs["private-cloud-2"].SecretKey != "QINIU_SECRET_KEY_3" { + t.Fatal("Unexpected secret key") + } + if bucketUrls, ok := profileConfigs["private-cloud-2"].BucketURL.([]interface{}); !ok { + t.Fatal("Unexpected bucket url") + } else { + if bucketUrls[0].(string) != "https://uc.qbox.me" { + t.Fatal("Unexpected bucket url") + } + if bucketUrls[1].(string) != "https://uc.qiniuapi.com" { + t.Fatal("Unexpected bucket url") + } + } + if profileConfigs["private-cloud-2"].DisableSecureProtocol { + t.Fatal("Unexpected disable secure protocol") + } + + os.Setenv("QINIU_PROFILE", "private-cloud") + defer os.Unsetenv("QINIU_PROFILE") + + accessKey, secretKey, err := CredentialsFromConfigFile() + if err != nil { + t.Fatal(err) + } + if accessKey != "QINIU_ACCESS_KEY_2" { + t.Fatal("Unexpected access key") + } + if secretKey != "QINIU_SECRET_KEY_2" { + t.Fatal("Unexpected secret key") + } + + bucketUrls, err := BucketURLsFromConfigFile() + if err != nil { + t.Fatal(err) + } + if len(bucketUrls) != 1 { + t.Fatal("Unexpected bucket urls") + } + if bucketUrls[0] != "https://uc.qbox.me" { + t.Fatal("Unexpected bucket url") + } + + os.Setenv("QINIU_PROFILE", "private-cloud-2") + defer os.Unsetenv("QINIU_PROFILE") + + accessKey, secretKey, err = CredentialsFromConfigFile() + if err != nil { + t.Fatal(err) + } + if accessKey != "QINIU_ACCESS_KEY_3" { + t.Fatal("Unexpected access key") + } + if secretKey != "QINIU_SECRET_KEY_3" { + t.Fatal("Unexpected secret key") + } + + bucketUrls, err = BucketURLsFromConfigFile() + if err != nil { + t.Fatal(err) + } + if len(bucketUrls) != 2 { + t.Fatal("Unexpected bucket urls") + } + if bucketUrls[0] != "https://uc.qbox.me" { + t.Fatal("Unexpected bucket url") + } + if bucketUrls[1] != "https://uc.qiniuapi.com" { + t.Fatal("Unexpected bucket url") + } +} diff --git a/internal/env/env.go b/internal/env/env.go new file mode 100644 index 00000000..2f58ff7b --- /dev/null +++ b/internal/env/env.go @@ -0,0 +1,61 @@ +package env + +import ( + "os" + "strings" +) + +const ( + environmentVariableNameQiniuAccessKey = "QINIU_ACCESS_KEY" + environmentVariableNameQiniuSecretKey = "QINIU_SECRET_KEY" + environmentVariableNameQiniuConfigFile = "QINIU_CONFIG_FILE" + environmentVariableNameQiniuProfile = "QINIU_PROFILE" + environmentVariableNameQiniuBucketURL = "QINIU_BUCKET_URL" + environmentVariableNameDisableQiniuSecureProtocol = "DISABLE_QINIU_SECURE_PROTOCOL" + environmentVariableNameDisableQiniuTimestampSignature = "DISABLE_QINIU_TIMESTAMP_SIGNATURE" +) + +func CredentialsFromEnvironment() (string, string) { + accessKey := os.Getenv(environmentVariableNameQiniuAccessKey) + secretKey := os.Getenv(environmentVariableNameQiniuSecretKey) + if accessKey == "" || secretKey == "" { + return "", "" + } + return accessKey, secretKey +} + +func ConfigFileFromEnvironment() string { + return os.Getenv(environmentVariableNameQiniuConfigFile) +} + +func ProfileFromEnvironment() string { + return os.Getenv(environmentVariableNameQiniuProfile) +} + +func BucketURLsFromEnvironment() []string { + bucketUrls := os.Getenv(environmentVariableNameQiniuBucketURL) + if bucketUrls == "" { + return nil + } + urls := strings.Split(bucketUrls, ",") + for i := range urls { + urls[i] = strings.TrimSpace(urls[i]) + } + return urls +} + +func DisableSecureProtocolFromEnvironment() (bool, bool) { + value := strings.ToLower(os.Getenv(environmentVariableNameDisableQiniuSecureProtocol)) + if value == "" { + return false, false + } + return value == "true" || value == "yes" || value == "y" || value == "1", true +} + +func DisableQiniuTimestampSignatureFromEnvironment() (bool, bool) { + value := strings.ToLower(os.Getenv(environmentVariableNameDisableQiniuTimestampSignature)) + if value == "" { + return false, false + } + return value == "true" || value == "yes" || value == "y" || value == "1", true +} diff --git a/sms/manager.go b/sms/manager.go index 747ce421..17701529 100644 --- a/sms/manager.go +++ b/sms/manager.go @@ -6,6 +6,7 @@ import ( "github.com/qiniu/go-sdk/v7/auth" "github.com/qiniu/go-sdk/v7/sms/client" "github.com/qiniu/go-sdk/v7/sms/rpc" + "github.com/qiniu/go-sdk/v7/storagev2/defaults" ) var ( @@ -24,9 +25,14 @@ func NewManager(mac *auth.Credentials) (manager *Manager) { manager = &Manager{} + if mac == nil { + if accessKey, secretKey, err := defaults.Credentials(); err == nil && accessKey != "" && secretKey != "" { + mac = auth.New(accessKey, secretKey) + } + } mac1 := &client.Mac{ AccessKey: mac.AccessKey, - SecretKey: []byte(mac.SecretKey), + SecretKey: mac.SecretKey, } transport := client.NewTransport(mac1, nil) diff --git a/storage/base64_upload.go b/storage/base64_upload.go index b205f4b2..f4e785a0 100644 --- a/storage/base64_upload.go +++ b/storage/base64_upload.go @@ -14,6 +14,7 @@ import ( "github.com/qiniu/go-sdk/v7/conf" "github.com/qiniu/go-sdk/v7/internal/hostprovider" + "github.com/qiniu/go-sdk/v7/storagev2/defaults" "github.com/qiniu/go-sdk/v7/client" ) @@ -26,20 +27,16 @@ type Base64Uploader struct { // NewBase64Uploader 用来构建一个Base64上传的对象 func NewBase64Uploader(cfg *Config) *Base64Uploader { - if cfg == nil { - cfg = &Config{} - } - - return &Base64Uploader{ - client: &client.DefaultClient, - cfg: cfg, - } + return NewBase64UploaderEx(cfg, nil) } // NewBase64UploaderEx 用来构建一个Base64上传的对象 func NewBase64UploaderEx(cfg *Config, clt *client.Client) *Base64Uploader { if cfg == nil { cfg = &Config{} + if isDisabled, err := defaults.DisableSecureProtocol(); err == nil { + cfg.UseHTTPS = !isDisabled + } } if clt == nil { diff --git a/storage/bucket.go b/storage/bucket.go index 9cb1b86d..b1d4fa28 100644 --- a/storage/bucket.go +++ b/storage/bucket.go @@ -17,6 +17,7 @@ import ( "github.com/qiniu/go-sdk/v7/internal/clientv2" "github.com/qiniu/go-sdk/v7/storagev2/apis" "github.com/qiniu/go-sdk/v7/storagev2/apis/batch_ops" + "github.com/qiniu/go-sdk/v7/storagev2/defaults" "github.com/qiniu/go-sdk/v7/storagev2/http_client" "github.com/qiniu/go-sdk/v7/auth" @@ -311,6 +312,14 @@ func NewBucketManagerEx(mac *auth.Credentials, cfg *Config, clt *clientv1.Client func NewBucketManagerExWithOptions(mac *auth.Credentials, cfg *Config, clt *clientv1.Client, options BucketManagerOptions) *BucketManager { if cfg == nil { cfg = &Config{} + if isDisabled, err := defaults.DisableSecureProtocol(); err == nil { + cfg.UseHTTPS = !isDisabled + } + } + if mac == nil { + if accessKey, secretKey, err := defaults.Credentials(); err == nil && accessKey != "" && secretKey != "" { + mac = auth.New(accessKey, secretKey) + } } if clt == nil { diff --git a/storage/form_upload.go b/storage/form_upload.go index c3f09838..3d60deeb 100644 --- a/storage/form_upload.go +++ b/storage/form_upload.go @@ -15,6 +15,7 @@ import ( "github.com/qiniu/go-sdk/v7/client" internal_io "github.com/qiniu/go-sdk/v7/internal/io" "github.com/qiniu/go-sdk/v7/storagev2/apis" + "github.com/qiniu/go-sdk/v7/storagev2/defaults" "github.com/qiniu/go-sdk/v7/storagev2/http_client" "github.com/qiniu/go-sdk/v7/storagev2/uptoken" ) @@ -73,6 +74,9 @@ func NewFormUploader(cfg *Config) *FormUploader { func NewFormUploaderEx(cfg *Config, clt *client.Client) *FormUploader { if cfg == nil { cfg = &Config{} + if isDisabled, err := defaults.DisableSecureProtocol(); err == nil { + cfg.UseHTTPS = !isDisabled + } } if clt == nil { diff --git a/storage/pfop.go b/storage/pfop.go index d7afca6b..3f8053f6 100644 --- a/storage/pfop.go +++ b/storage/pfop.go @@ -9,6 +9,7 @@ import ( "github.com/qiniu/go-sdk/v7/auth" "github.com/qiniu/go-sdk/v7/client" "github.com/qiniu/go-sdk/v7/conf" + "github.com/qiniu/go-sdk/v7/storagev2/defaults" ) // OperationManager 提供了数据处理相关的方法 @@ -20,21 +21,21 @@ type OperationManager struct { // NewOperationManager 用来构建一个新的数据处理对象 func NewOperationManager(mac *auth.Credentials, cfg *Config) *OperationManager { - if cfg == nil { - cfg = &Config{} - } - - return &OperationManager{ - Client: &client.DefaultClient, - Mac: mac, - Cfg: cfg, - } + return NewOperationManagerEx(mac, cfg, nil) } // NewOperationManager 用来构建一个新的数据处理对象 func NewOperationManagerEx(mac *auth.Credentials, cfg *Config, clt *client.Client) *OperationManager { if cfg == nil { cfg = &Config{} + if isDisabled, err := defaults.DisableSecureProtocol(); err == nil { + cfg.UseHTTPS = !isDisabled + } + } + if mac == nil { + if accessKey, secretKey, err := defaults.Credentials(); err == nil && accessKey != "" && secretKey != "" { + mac = auth.New(accessKey, secretKey) + } } if clt == nil { diff --git a/storage/region.go b/storage/region.go index 6b34958b..e0a4358e 100644 --- a/storage/region.go +++ b/storage/region.go @@ -11,6 +11,7 @@ import ( "github.com/qiniu/go-sdk/v7/internal/clientv2" "github.com/qiniu/go-sdk/v7/internal/hostprovider" "github.com/qiniu/go-sdk/v7/storagev2/apis" + "github.com/qiniu/go-sdk/v7/storagev2/defaults" "github.com/qiniu/go-sdk/v7/storagev2/http_client" region_v2 "github.com/qiniu/go-sdk/v7/storagev2/region" ) @@ -241,6 +242,12 @@ var UcHost = "" // 用户配置时,不能配置 api 域名 var ucHosts = []string{defaultUcHost0, defaultUcHost1, defaultApiHost} +func init() { + if defaultUcHosts, err := defaults.BucketURLs(); err == nil && len(defaultUcHosts) > 0 { + ucHosts = defaultUcHosts + } +} + // SetUcHost // Deprecated 使用 SetUcHosts 替换 func SetUcHost(host string, useHttps bool) { @@ -347,14 +354,20 @@ func GetRegionsInfo(mac *auth.Credentials) ([]RegionInfo, error) { } func GetRegionsInfoWithOptions(mac *auth.Credentials, options UCApiOptions) ([]RegionInfo, error) { + if mac == nil { + if accessKey, secretKey, err := defaults.Credentials(); err == nil && accessKey != "" && secretKey != "" { + mac = auth.New(accessKey, secretKey) + } + } response, err := apis.NewStorage(&http_client.Options{ + Credentials: mac, HostFreezeDuration: options.HostFreezeDuration, HostRetryConfig: &clientv2.RetryConfig{ RetryMax: options.RetryMax, }, }).GetRegions( context.Background(), - &apis.GetRegionsRequest{Credentials: mac}, + &apis.GetRegionsRequest{}, &apis.Options{OverwrittenBucketHosts: getUcEndpoint(options.UseHttps)}, ) if err != nil { diff --git a/storage/resume_uploader.go b/storage/resume_uploader.go index 5795d31b..2701dee6 100644 --- a/storage/resume_uploader.go +++ b/storage/resume_uploader.go @@ -14,6 +14,7 @@ import ( "github.com/qiniu/go-sdk/v7/client" "github.com/qiniu/go-sdk/v7/internal/clientv2" "github.com/qiniu/go-sdk/v7/storagev2/apis" + "github.com/qiniu/go-sdk/v7/storagev2/defaults" "github.com/qiniu/go-sdk/v7/storagev2/http_client" "github.com/qiniu/go-sdk/v7/storagev2/region" ) @@ -34,6 +35,9 @@ func NewResumeUploader(cfg *Config) *ResumeUploader { func NewResumeUploaderEx(cfg *Config, clt *client.Client) *ResumeUploader { if cfg == nil { cfg = &Config{} + if isDisabled, err := defaults.DisableSecureProtocol(); err == nil { + cfg.UseHTTPS = !isDisabled + } } if clt == nil { diff --git a/storage/resume_uploader_v2.go b/storage/resume_uploader_v2.go index edd5d324..3aeb2881 100644 --- a/storage/resume_uploader_v2.go +++ b/storage/resume_uploader_v2.go @@ -15,6 +15,7 @@ import ( "github.com/qiniu/go-sdk/v7/client" "github.com/qiniu/go-sdk/v7/internal/clientv2" "github.com/qiniu/go-sdk/v7/storagev2/apis" + "github.com/qiniu/go-sdk/v7/storagev2/defaults" "github.com/qiniu/go-sdk/v7/storagev2/http_client" "github.com/qiniu/go-sdk/v7/storagev2/region" ) @@ -35,6 +36,9 @@ func NewResumeUploaderV2(cfg *Config) *ResumeUploaderV2 { func NewResumeUploaderV2Ex(cfg *Config, clt *client.Client) *ResumeUploaderV2 { if cfg == nil { cfg = &Config{} + if isDisabled, err := defaults.DisableSecureProtocol(); err == nil { + cfg.UseHTTPS = !isDisabled + } } if clt == nil { diff --git a/storage/upload_manager.go b/storage/upload_manager.go index b461765a..8778a76f 100644 --- a/storage/upload_manager.go +++ b/storage/upload_manager.go @@ -10,6 +10,7 @@ import ( "time" "github.com/qiniu/go-sdk/v7/client" + "github.com/qiniu/go-sdk/v7/storagev2/defaults" ) type UploadResumeVersion = int @@ -155,6 +156,9 @@ func NewUploadManager(cfg *UploadConfig) *UploadManager { func NewUploadManagerEx(cfg *UploadConfig, c *client.Client) *UploadManager { if cfg == nil { cfg = &UploadConfig{} + if isDisabled, err := defaults.DisableSecureProtocol(); err == nil { + cfg.UseHTTPS = !isDisabled + } } if c == nil { diff --git a/storagev2/defaults/defaults.go b/storagev2/defaults/defaults.go new file mode 100644 index 00000000..c896bbfc --- /dev/null +++ b/storagev2/defaults/defaults.go @@ -0,0 +1,69 @@ +package defaults + +import ( + "strings" + + "github.com/qiniu/go-sdk/v7/internal/configfile" + "github.com/qiniu/go-sdk/v7/internal/env" +) + +func Credentials() (string, string, error) { + accessKey, secretKey := env.CredentialsFromEnvironment() + if accessKey != "" && secretKey != "" { + return accessKey, secretKey, nil + } + accessKey, secretKey, err := configfile.CredentialsFromConfigFile() + if err != nil { + return "", "", err + } + if accessKey != "" && secretKey != "" { + return accessKey, secretKey, nil + } + return "", "", nil +} + +func BucketURLs() ([]string, error) { + normalizeBucketUrl := func(bucketUrl string) string { + if !strings.Contains(bucketUrl, "://") { + var ( + isDisabled bool + err error + ) + if isDisabled, err = DisableSecureProtocol(); err != nil { + isDisabled = false + } + if isDisabled { + bucketUrl = "http://" + bucketUrl + } else { + bucketUrl = "https://" + bucketUrl + } + } + return bucketUrl + } + + normalizeBucketUrls := func(bucketUrls []string) []string { + normalizedBucketUrls := make([]string, 0, len(bucketUrls)) + for _, bucketUrl := range bucketUrls { + normalizedBucketUrls = append(normalizedBucketUrls, normalizeBucketUrl(bucketUrl)) + } + return normalizedBucketUrls + } + + bucketUrls := env.BucketURLsFromEnvironment() + if bucketUrls != nil { + return normalizeBucketUrls(bucketUrls), nil + } + bucketUrls, err := configfile.BucketURLsFromConfigFile() + if err != nil { + return nil, err + } + return normalizeBucketUrls(bucketUrls), nil +} + +func DisableSecureProtocol() (bool, error) { + isDisabled, ok := env.DisableSecureProtocolFromEnvironment() + if ok { + return isDisabled, nil + } + return configfile.DisableSecureProtocolFromConfigFile() +} diff --git a/storagev2/http_client/http_client.go b/storagev2/http_client/http_client.go index e215abab..fe57a361 100644 --- a/storagev2/http_client/http_client.go +++ b/storagev2/http_client/http_client.go @@ -15,6 +15,7 @@ import ( "github.com/qiniu/go-sdk/v7/internal/hostprovider" compatible_io "github.com/qiniu/go-sdk/v7/internal/io" "github.com/qiniu/go-sdk/v7/storagev2/credentials" + "github.com/qiniu/go-sdk/v7/storagev2/defaults" "github.com/qiniu/go-sdk/v7/storagev2/region" "github.com/qiniu/go-sdk/v7/storagev2/uptoken" ) @@ -81,6 +82,9 @@ type ( func NewClient(options *Options) *Client { if options == nil { options = &Options{} + if isDisabled, err := defaults.DisableSecureProtocol(); err == nil { + options.UseInsecureProtocol = isDisabled + } } if options.HostFreezeDuration < time.Millisecond { options.HostFreezeDuration = 600 * time.Second @@ -88,6 +92,11 @@ func NewClient(options *Options) *Client { if options.ShouldFreezeHost == nil { options.ShouldFreezeHost = defaultShouldFreezeHost } + if options.Credentials == nil { + if accessKey, secretKey, err := defaults.Credentials(); err == nil && accessKey != "" && secretKey != "" { + options.Credentials = credentials.NewCredentials(accessKey, secretKey) + } + } return &Client{ basicHTTPClient: clientv2.NewClient(options.BasicHTTPClient, options.Interceptors...), @@ -312,6 +321,12 @@ var defaultBucketHosts = region.Endpoints{ Alternative: []string{"uc.qbox.me"}, } +func init() { + if bucketUrls, err := defaults.BucketURLs(); err == nil && len(bucketUrls) > 0 { + defaultBucketHosts = region.Endpoints{Preferred: bucketUrls} + } +} + // DefaultBucketHosts 默认的 Bucket 域名列表 func DefaultBucketHosts() region.Endpoints { return defaultBucketHosts.Clone() diff --git a/storagev2/uptoken/uploadtoken.go b/storagev2/uptoken/uploadtoken.go index 78d9302d..c5d0ccbc 100644 --- a/storagev2/uptoken/uploadtoken.go +++ b/storagev2/uptoken/uploadtoken.go @@ -9,6 +9,7 @@ import ( "sync" "github.com/qiniu/go-sdk/v7/storagev2/credentials" + "github.com/qiniu/go-sdk/v7/storagev2/defaults" ) // ErrInvalidUpToken 非法的上传凭证 @@ -75,7 +76,13 @@ func (signer *signer) GetUpToken(ctx context.Context) (string, error) { func (signer *signer) onceGetCredentials(ctx context.Context) (*credentials.Credentials, error) { var err error signer.onceCredentials.Do(func() { - signer.credentials, err = signer.credentialsProvider.Get(ctx) + if signer.credentialsProvider != nil { + signer.credentials, err = signer.credentialsProvider.Get(ctx) + } else if accessKey, secretKey, e := defaults.Credentials(); e != nil { + err = e + } else if accessKey != "" && secretKey != "" { + signer.credentials = credentials.NewCredentials(accessKey, secretKey) + } }) return signer.credentials, err } diff --git a/storagev2/uptoken/uploadtoken_test.go b/storagev2/uptoken/uploadtoken_test.go index 3560ebb2..ade78ddd 100644 --- a/storagev2/uptoken/uploadtoken_test.go +++ b/storagev2/uptoken/uploadtoken_test.go @@ -5,6 +5,7 @@ package uptoken_test import ( "context" + "os" "testing" "time" @@ -50,3 +51,26 @@ func TestSignPutPolicy(t *testing.T) { t.Fatalf("unexpected scope: %s", actualScope) } } + +func TestSignPutPolicyByDefaultCredentials(t *testing.T) { + const expectedBucketName = "testbucket" + const expectedAccessKey = "testaccesskey" + const expectedSecretKey = "testsecretkey" + const expectedExpires = int64(1675937798) + + os.Setenv("QINIU_ACCESS_KEY", expectedAccessKey) + os.Setenv("QINIU_SECRET_KEY", expectedSecretKey) + defer func() { + os.Unsetenv("QINIU_ACCESS_KEY") + os.Unsetenv("QINIU_SECRET_KEY") + }() + + putPolicy, err := uptoken.NewPutPolicy(expectedBucketName, time.Unix(expectedExpires, 0)) + if err != nil { + t.Fatalf("failed to create put policy: %s", err) + } + signer := uptoken.NewSigner(putPolicy, nil) + if accessKey, _ := signer.GetAccessKey(context.Background()); accessKey != "testaccesskey" { + t.Fatalf("failed to retrieve accessKey: %s", err) + } +}