github.com/aliyun/credentials-go@v1.4.7/credentials/providers/cloud_sso.go (about) 1 package providers 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "net/http" 8 "net/url" 9 "time" 10 11 httputil "github.com/aliyun/credentials-go/credentials/internal/http" 12 ) 13 14 type CloudSSOCredentialsProvider struct { 15 signInUrl string 16 accountId string 17 accessConfig string 18 accessToken string 19 accessTokenExpire int64 20 21 lastUpdateTimestamp int64 22 expirationTimestamp int64 23 sessionCredentials *sessionCredentials 24 // for http options 25 httpOptions *HttpOptions 26 } 27 28 type CloudSSOCredentialsProviderBuilder struct { 29 provider *CloudSSOCredentialsProvider 30 } 31 32 type cloudCredentialOptions struct { 33 AccountId string `json:"AccountId"` 34 AccessConfigurationId string `json:"AccessConfigurationId"` 35 } 36 37 type cloudCredentials struct { 38 AccessKeyId string `json:"AccessKeyId"` 39 AccessKeySecret string `json:"AccessKeySecret"` 40 SecurityToken string `json:"SecurityToken"` 41 Expiration string `json:"Expiration"` 42 } 43 44 type cloudCredentialResponse struct { 45 CloudCredential *cloudCredentials `json:"CloudCredential"` 46 RequestId string `json:"RequestId"` 47 } 48 49 func NewCloudSSOCredentialsProviderBuilder() *CloudSSOCredentialsProviderBuilder { 50 return &CloudSSOCredentialsProviderBuilder{ 51 provider: &CloudSSOCredentialsProvider{}, 52 } 53 } 54 55 func (b *CloudSSOCredentialsProviderBuilder) WithSignInUrl(signInUrl string) *CloudSSOCredentialsProviderBuilder { 56 b.provider.signInUrl = signInUrl 57 return b 58 } 59 60 func (b *CloudSSOCredentialsProviderBuilder) WithAccountId(accountId string) *CloudSSOCredentialsProviderBuilder { 61 b.provider.accountId = accountId 62 return b 63 } 64 65 func (b *CloudSSOCredentialsProviderBuilder) WithAccessConfig(accessConfig string) *CloudSSOCredentialsProviderBuilder { 66 b.provider.accessConfig = accessConfig 67 return b 68 } 69 70 func (b *CloudSSOCredentialsProviderBuilder) WithAccessToken(accessToken string) *CloudSSOCredentialsProviderBuilder { 71 b.provider.accessToken = accessToken 72 return b 73 } 74 75 func (b *CloudSSOCredentialsProviderBuilder) WithAccessTokenExpire(accessTokenExpire int64) *CloudSSOCredentialsProviderBuilder { 76 b.provider.accessTokenExpire = accessTokenExpire 77 return b 78 } 79 80 func (b *CloudSSOCredentialsProviderBuilder) WithHttpOptions(httpOptions *HttpOptions) *CloudSSOCredentialsProviderBuilder { 81 b.provider.httpOptions = httpOptions 82 return b 83 } 84 85 func (b *CloudSSOCredentialsProviderBuilder) Build() (provider *CloudSSOCredentialsProvider, err error) { 86 if b.provider.accessToken == "" || b.provider.accessTokenExpire == 0 || b.provider.accessTokenExpire-time.Now().Unix() <= 0 { 87 err = errors.New("CloudSSO access token is empty or expired, please re-login with cli") 88 return 89 } 90 91 if b.provider.signInUrl == "" || b.provider.accountId == "" || b.provider.accessConfig == "" { 92 err = errors.New("CloudSSO sign in url or account id or access config is empty") 93 return 94 } 95 96 provider = b.provider 97 return 98 } 99 100 func (provider *CloudSSOCredentialsProvider) getCredentials() (session *sessionCredentials, err error) { 101 url, err := url.Parse(provider.signInUrl) 102 if err != nil { 103 return nil, err 104 } 105 106 req := &httputil.Request{ 107 Method: "POST", 108 Protocol: url.Scheme, 109 Host: url.Host, 110 Path: "/cloud-credentials", 111 Headers: map[string]string{}, 112 } 113 114 connectTimeout := 5 * time.Second 115 readTimeout := 10 * time.Second 116 117 if provider.httpOptions != nil && provider.httpOptions.ConnectTimeout > 0 { 118 connectTimeout = time.Duration(provider.httpOptions.ConnectTimeout) * time.Millisecond 119 } 120 if provider.httpOptions != nil && provider.httpOptions.ReadTimeout > 0 { 121 readTimeout = time.Duration(provider.httpOptions.ReadTimeout) * time.Millisecond 122 } 123 if provider.httpOptions != nil && provider.httpOptions.Proxy != "" { 124 req.Proxy = provider.httpOptions.Proxy 125 } 126 req.ConnectTimeout = connectTimeout 127 req.ReadTimeout = readTimeout 128 129 body := cloudCredentialOptions{ 130 AccountId: provider.accountId, 131 AccessConfigurationId: provider.accessConfig, 132 } 133 134 bodyBytes, err := json.Marshal(body) 135 if err != nil { 136 return nil, fmt.Errorf("failed to marshal options: %w", err) 137 } 138 139 req.Body = bodyBytes 140 141 // set headers 142 req.Headers["Accept"] = "application/json" 143 req.Headers["Content-Type"] = "application/json" 144 req.Headers["Authorization"] = fmt.Sprintf("Bearer %s", provider.accessToken) 145 res, err := httpDo(req) 146 if err != nil { 147 return 148 } 149 150 if res.StatusCode != http.StatusOK { 151 message := "get session token from sso failed: " 152 err = errors.New(message + string(res.Body)) 153 return 154 } 155 var data cloudCredentialResponse 156 err = json.Unmarshal(res.Body, &data) 157 if err != nil { 158 err = fmt.Errorf("get session token from sso failed, json.Unmarshal fail: %s", err.Error()) 159 return 160 } 161 if data.CloudCredential == nil { 162 err = fmt.Errorf("get session token from sso failed, fail to get credentials") 163 return 164 } 165 166 if data.CloudCredential.AccessKeyId == "" || data.CloudCredential.AccessKeySecret == "" || data.CloudCredential.SecurityToken == "" { 167 err = fmt.Errorf("refresh session token err, fail to get credentials") 168 return 169 } 170 171 session = &sessionCredentials{ 172 AccessKeyId: data.CloudCredential.AccessKeyId, 173 AccessKeySecret: data.CloudCredential.AccessKeySecret, 174 SecurityToken: data.CloudCredential.SecurityToken, 175 Expiration: data.CloudCredential.Expiration, 176 } 177 return 178 } 179 180 func (provider *CloudSSOCredentialsProvider) needUpdateCredential() (result bool) { 181 if provider.expirationTimestamp == 0 { 182 return true 183 } 184 185 return provider.expirationTimestamp-time.Now().Unix() <= 180 186 } 187 188 func (provider *CloudSSOCredentialsProvider) GetCredentials() (cc *Credentials, err error) { 189 if provider.sessionCredentials == nil || provider.needUpdateCredential() { 190 sessionCredentials, err1 := provider.getCredentials() 191 if err1 != nil { 192 return nil, err1 193 } 194 195 provider.sessionCredentials = sessionCredentials 196 expirationTime, err2 := time.Parse("2006-01-02T15:04:05Z", sessionCredentials.Expiration) 197 if err2 != nil { 198 return nil, err2 199 } 200 201 provider.lastUpdateTimestamp = time.Now().Unix() 202 provider.expirationTimestamp = expirationTime.Unix() 203 } 204 205 cc = &Credentials{ 206 AccessKeyId: provider.sessionCredentials.AccessKeyId, 207 AccessKeySecret: provider.sessionCredentials.AccessKeySecret, 208 SecurityToken: provider.sessionCredentials.SecurityToken, 209 ProviderName: provider.GetProviderName(), 210 } 211 return 212 } 213 214 func (provider *CloudSSOCredentialsProvider) GetProviderName() string { 215 return "cloud_sso" 216 }