1/* 2 * Licensed under the Apache License, Version 2.0 (the "License"); 3 * you may not use this file except in compliance with the License. 4 * You may obtain a copy of the License at 5 * 6 * http://www.apache.org/licenses/LICENSE-2.0 7 * 8 * Unless required by applicable law or agreed to in writing, software 9 * distributed under the License is distributed on an "AS IS" BASIS, 10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 * See the License for the specific language governing permissions and 12 * limitations under the License. 13 */ 14 15package signers 16 17import ( 18 "encoding/json" 19 "fmt" 20 "net/http" 21 "strconv" 22 "time" 23 24 "github.com/aliyun/alibaba-cloud-sdk-go/sdk/auth/credentials" 25 "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" 26 "github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests" 27 "github.com/aliyun/alibaba-cloud-sdk-go/sdk/responses" 28 jmespath "github.com/jmespath/go-jmespath" 29) 30 31const ( 32 defaultDurationSeconds = 3600 33) 34 35type RamRoleArnSigner struct { 36 *credentialUpdater 37 roleSessionName string 38 sessionCredential *SessionCredential 39 credential *credentials.RamRoleArnCredential 40 commonApi func(request *requests.CommonRequest, signer interface{}) (response *responses.CommonResponse, err error) 41} 42 43func NewRamRoleArnSigner(credential *credentials.RamRoleArnCredential, commonApi func(request *requests.CommonRequest, signer interface{}) (response *responses.CommonResponse, err error)) (signer *RamRoleArnSigner, err error) { 44 signer = &RamRoleArnSigner{ 45 credential: credential, 46 commonApi: commonApi, 47 } 48 49 signer.credentialUpdater = &credentialUpdater{ 50 credentialExpiration: credential.RoleSessionExpiration, 51 buildRequestMethod: signer.buildCommonRequest, 52 responseCallBack: signer.refreshCredential, 53 refreshApi: signer.refreshApi, 54 } 55 56 if len(credential.RoleSessionName) > 0 { 57 signer.roleSessionName = credential.RoleSessionName 58 } else { 59 signer.roleSessionName = "aliyun-go-sdk-" + strconv.FormatInt(time.Now().UnixNano()/1000, 10) 60 } 61 if credential.RoleSessionExpiration > 0 { 62 if credential.RoleSessionExpiration >= 900 && credential.RoleSessionExpiration <= 3600 { 63 signer.credentialExpiration = credential.RoleSessionExpiration 64 } else { 65 err = errors.NewClientError(errors.InvalidParamErrorCode, "Assume Role session duration should be in the range of 15min - 1Hr", nil) 66 } 67 } else { 68 signer.credentialExpiration = defaultDurationSeconds 69 } 70 return 71} 72 73func (*RamRoleArnSigner) GetName() string { 74 return "HMAC-SHA1" 75} 76 77func (*RamRoleArnSigner) GetType() string { 78 return "" 79} 80 81func (*RamRoleArnSigner) GetVersion() string { 82 return "1.0" 83} 84 85func (signer *RamRoleArnSigner) GetAccessKeyId() (accessKeyId string, err error) { 86 if signer.sessionCredential == nil || signer.needUpdateCredential() { 87 err = signer.updateCredential() 88 if err != nil { 89 return 90 } 91 } 92 93 if signer.sessionCredential == nil || len(signer.sessionCredential.AccessKeyId) <= 0 { 94 return "", err 95 } 96 97 return signer.sessionCredential.AccessKeyId, nil 98} 99 100func (signer *RamRoleArnSigner) GetExtraParam() map[string]string { 101 if signer.sessionCredential == nil || signer.needUpdateCredential() { 102 signer.updateCredential() 103 } 104 if signer.sessionCredential == nil || len(signer.sessionCredential.StsToken) <= 0 { 105 return make(map[string]string) 106 } 107 return map[string]string{"SecurityToken": signer.sessionCredential.StsToken} 108} 109 110func (signer *RamRoleArnSigner) Sign(stringToSign, secretSuffix string) string { 111 secret := signer.sessionCredential.AccessKeySecret + secretSuffix 112 return ShaHmac1(stringToSign, secret) 113} 114 115func (signer *RamRoleArnSigner) buildCommonRequest() (request *requests.CommonRequest, err error) { 116 request = requests.NewCommonRequest() 117 if signer.credential.StsRegion != "" { 118 request.Domain = fmt.Sprintf("sts.%s.aliyuncs.com", signer.credential.StsRegion) 119 } else { 120 request.Domain = "sts.aliyuncs.com" 121 } 122 request.Product = "Sts" 123 request.Version = "2015-04-01" 124 request.ApiName = "AssumeRole" 125 request.Scheme = requests.HTTPS 126 request.QueryParams["RoleArn"] = signer.credential.RoleArn 127 if signer.credential.Policy != "" { 128 request.QueryParams["Policy"] = signer.credential.Policy 129 } 130 request.QueryParams["RoleSessionName"] = signer.credential.RoleSessionName 131 request.QueryParams["DurationSeconds"] = strconv.Itoa(signer.credentialExpiration) 132 return 133} 134 135func (signer *RamRoleArnSigner) refreshApi(request *requests.CommonRequest) (response *responses.CommonResponse, err error) { 136 credential := &credentials.AccessKeyCredential{ 137 AccessKeyId: signer.credential.AccessKeyId, 138 AccessKeySecret: signer.credential.AccessKeySecret, 139 } 140 signerV1 := NewAccessKeySigner(credential) 141 return signer.commonApi(request, signerV1) 142} 143 144func (signer *RamRoleArnSigner) refreshCredential(response *responses.CommonResponse) (err error) { 145 if response.GetHttpStatus() != http.StatusOK { 146 message := "refresh session token failed" 147 err = errors.NewServerError(response.GetHttpStatus(), response.GetHttpContentString(), message) 148 return 149 } 150 var data interface{} 151 err = json.Unmarshal(response.GetHttpContentBytes(), &data) 152 if err != nil { 153 return fmt.Errorf("refresh RoleArn sts token err, json.Unmarshal fail: %s", err.Error()) 154 } 155 accessKeyId, err := jmespath.Search("Credentials.AccessKeyId", data) 156 if err != nil { 157 return fmt.Errorf("refresh RoleArn sts token err, fail to get AccessKeyId: %s", err.Error()) 158 } 159 accessKeySecret, err := jmespath.Search("Credentials.AccessKeySecret", data) 160 if err != nil { 161 return fmt.Errorf("refresh RoleArn sts token err, fail to get AccessKeySecret: %s", err.Error()) 162 } 163 securityToken, err := jmespath.Search("Credentials.SecurityToken", data) 164 if err != nil { 165 return fmt.Errorf("refresh RoleArn sts token err, fail to get SecurityToken: %s", err.Error()) 166 } 167 if accessKeyId == nil || accessKeySecret == nil || securityToken == nil { 168 return 169 } 170 signer.sessionCredential = &SessionCredential{ 171 AccessKeyId: accessKeyId.(string), 172 AccessKeySecret: accessKeySecret.(string), 173 StsToken: securityToken.(string), 174 } 175 return 176} 177 178func (signer *RamRoleArnSigner) GetSessionCredential() *SessionCredential { 179 return signer.sessionCredential 180} 181