credentials/providers/uri.go (123 lines of code) (raw):
package providers
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"time"
httputil "github.com/aliyun/credentials-go/credentials/internal/http"
)
type URLCredentialsProvider struct {
url string
// for sts
sessionCredentials *sessionCredentials
// for http options
httpOptions *HttpOptions
// inner
expirationTimestamp int64
}
type URLCredentialsProviderBuilder struct {
provider *URLCredentialsProvider
}
func NewURLCredentialsProviderBuilder() *URLCredentialsProviderBuilder {
return &URLCredentialsProviderBuilder{
provider: &URLCredentialsProvider{},
}
}
func (builder *URLCredentialsProviderBuilder) WithUrl(url string) *URLCredentialsProviderBuilder {
builder.provider.url = url
return builder
}
func (builder *URLCredentialsProviderBuilder) WithHttpOptions(httpOptions *HttpOptions) *URLCredentialsProviderBuilder {
builder.provider.httpOptions = httpOptions
return builder
}
func (builder *URLCredentialsProviderBuilder) Build() (provider *URLCredentialsProvider, err error) {
if builder.provider.url == "" {
builder.provider.url = os.Getenv("ALIBABA_CLOUD_CREDENTIALS_URI")
}
if builder.provider.url == "" {
err = errors.New("the url is empty")
return
}
provider = builder.provider
return
}
type urlResponse struct {
AccessKeyId *string `json:"AccessKeyId"`
AccessKeySecret *string `json:"AccessKeySecret"`
SecurityToken *string `json:"SecurityToken"`
Expiration *string `json:"Expiration"`
}
func (provider *URLCredentialsProvider) getCredentials() (session *sessionCredentials, err error) {
req := &httputil.Request{
Method: "GET",
URL: provider.url,
}
connectTimeout := 5 * time.Second
readTimeout := 10 * time.Second
if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 {
connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond
}
if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 {
readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond
}
if provider.httpOptions != nil && provider.httpOptions.Proxy != "" {
req.Proxy = provider.httpOptions.Proxy
}
req.ConnectTimeout = connectTimeout
req.ReadTimeout = readTimeout
res, err := httpDo(req)
if err != nil {
return
}
if res.StatusCode != http.StatusOK {
err = fmt.Errorf("get credentials from %s failed: %s", req.BuildRequestURL(), string(res.Body))
return
}
var resp urlResponse
err = json.Unmarshal(res.Body, &resp)
if err != nil {
err = fmt.Errorf("get credentials from %s failed with error, json unmarshal fail: %s", req.BuildRequestURL(), err.Error())
return
}
if resp.AccessKeyId == nil || resp.AccessKeySecret == nil || resp.SecurityToken == nil || resp.Expiration == nil {
err = fmt.Errorf("refresh credentials from %s failed: %s", req.BuildRequestURL(), string(res.Body))
return
}
session = &sessionCredentials{
AccessKeyId: *resp.AccessKeyId,
AccessKeySecret: *resp.AccessKeySecret,
SecurityToken: *resp.SecurityToken,
Expiration: *resp.Expiration,
}
return
}
func (provider *URLCredentialsProvider) needUpdateCredential() (result bool) {
if provider.expirationTimestamp == 0 {
return true
}
return provider.expirationTimestamp-time.Now().Unix() <= 180
}
func (provider *URLCredentialsProvider) GetCredentials() (cc *Credentials, err error) {
if provider.sessionCredentials == nil || provider.needUpdateCredential() {
sessionCredentials, err1 := provider.getCredentials()
if err1 != nil {
return nil, err1
}
provider.sessionCredentials = sessionCredentials
expirationTime, err2 := time.Parse("2006-01-02T15:04:05Z", sessionCredentials.Expiration)
if err2 != nil {
return nil, err2
}
provider.expirationTimestamp = expirationTime.Unix()
}
cc = &Credentials{
AccessKeyId: provider.sessionCredentials.AccessKeyId,
AccessKeySecret: provider.sessionCredentials.AccessKeySecret,
SecurityToken: provider.sessionCredentials.SecurityToken,
ProviderName: provider.GetProviderName(),
}
return
}
func (provider *URLCredentialsProvider) GetProviderName() string {
return "credential_uri"
}