cloudsso/refresh.go (125 lines of code) (raw):

// Copyright (c) 2009-present, Alibaba Cloud All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package cloudsso import ( "bytes" "encoding/json" "errors" "fmt" "github.com/aliyun/aliyun-cli/v3/cli" "io" "io/ioutil" "net/http" "net/url" "time" ) func TryRefreshStsToken(signInUrl *string, accessToken *string, accessConfig *string, accountId *string, client *http.Client) (*CloudCredentialResponse, error) { // parse signInUrl get host and protocol if signInUrl == nil || *signInUrl == "" { return nil, errors.New("signInUrl is empty") } parsedUrl, err := ParseUrl(signInUrl) if err != nil { return nil, err } if len(parsedUrl) != 2 { return nil, errors.New("invalid signInUrl") } host := parsedUrl[1] protocol := parsedUrl[0] // 使用传入的HTTP客户端,如果为nil则使用默认客户端 httpClient := client if httpClient == nil { httpClient = http.DefaultClient } credential, err := CreateCloudCredential(protocol+"://"+host, *accessToken, CloudCredentialOptions{ AccountId: *accountId, AccessConfigurationId: *accessConfig, }, httpClient) if err != nil { return nil, err } if credential == nil { return nil, errors.New("credential is nil") } return credential, nil } func ParseUrl(urlStr *string) ([]string, error) { if urlStr == nil || *urlStr == "" { return nil, errors.New("url is empty") } parsedUrl, err := url.Parse(*urlStr) if err != nil { return nil, err } host := parsedUrl.Host scheme := parsedUrl.Scheme if host == "" || scheme == "" { return nil, errors.New("invalid url: missing host or scheme") } return []string{scheme, host}, nil } type CloudCredentialOptions struct { AccountId string `json:"AccountId"` AccessConfigurationId string `json:"AccessConfigurationId"` } type CloudCredentialResponse struct { AccessKeyId string `json:"AccessKeyId"` AccessKeySecret string `json:"AccessKeySecret"` SecurityToken string `json:"SecurityToken"` Expiration string `json:"Expiration"` ExpirationInt64 int64 `json:"ExpirationInt64"` } type CloudCredentialResponseRaw struct { CloudCredential *CloudCredentialResponse `json:"CloudCredential"` RequestId string `json:"RequestId"` } func CreateCloudCredential(prefix string, accessToken string, options CloudCredentialOptions, client *http.Client) (*CloudCredentialResponse, error) { urlFetch := fmt.Sprintf("%s/cloud-credentials", prefix) // Prepare request body data, err := json.Marshal(options) if err != nil { return nil, fmt.Errorf("failed to marshal options: %w", err) } // Create HTTP request req, err := http.NewRequest("POST", urlFetch, bytes.NewBuffer(data)) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } // Set headers req.Header.Set("accept", "application/json") req.Header.Set("content-type", "application/json") req.Header.Set("authorization", fmt.Sprintf("Bearer %s", accessToken)) req.Header.Set("user-agent", "aliyun/CLI-"+cli.Version) // Send request resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("failed to send request: %w", err) } defer func(Body io.ReadCloser) { err := Body.Close() if err != nil { fmt.Printf("failed to close response body: %v", err) } }(resp.Body) // Read response body body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response body: %w", err) } // Handle HTTP errors if resp.StatusCode >= 400 && resp.StatusCode < 500 { bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read error response body: %w", err) } bodyString := string(bodyBytes) var errResp map[string]interface{} if err := json.Unmarshal(bodyBytes, &errResp); err != nil { // 如果解析 JSON 失败,返回原始响应体作为错误信息 return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, bodyString) } return nil, fmt.Errorf("HTTP %d: %s: %s %s", resp.StatusCode, bodyString, errResp["ErrorCode"], errResp["ErrorMessage"]) } // Parse successful response var result CloudCredentialResponseRaw if err := json.Unmarshal(body, &result); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } if result.CloudCredential.Expiration != "" { // Parse expiration time expiration, err := time.Parse(time.RFC3339, result.CloudCredential.Expiration) if err != nil { return nil, fmt.Errorf("failed to parse expiration time: %w", err) } result.CloudCredential.ExpirationInt64 = expiration.Unix() } return result.CloudCredential, nil }