sts.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package sts
  2. import (
  3. "crypto/hmac"
  4. "crypto/sha1"
  5. "crypto/tls"
  6. "encoding/base64"
  7. "encoding/json"
  8. "fmt"
  9. "io/ioutil"
  10. "net/http"
  11. "net/url"
  12. "strconv"
  13. "time"
  14. "github.com/satori/go.uuid"
  15. )
  16. // Client sts client
  17. type Client struct {
  18. AccessKeyId string
  19. AccessKeySecret string
  20. RoleArn string
  21. SessionName string
  22. }
  23. // ServiceError sts service error
  24. type ServiceError struct {
  25. Code string
  26. Message string
  27. RequestId string
  28. HostId string
  29. RawMessage string
  30. StatusCode int
  31. }
  32. // Credentials the credentials obtained by AssumedRole,
  33. // used for the peration of Alibaba Cloud service.
  34. type Credentials struct {
  35. AccessKeyId string
  36. AccessKeySecret string
  37. Expiration time.Time
  38. SecurityToken string
  39. }
  40. // AssumedRoleUser the user to AssumedRole
  41. type AssumedRoleUser struct {
  42. Arn string
  43. AssumedRoleId string
  44. }
  45. // Response the response of AssumeRole
  46. type Response struct {
  47. Credentials Credentials
  48. AssumedRoleUser AssumedRoleUser
  49. RequestId string
  50. }
  51. // Error implement interface error
  52. func (e *ServiceError) Error() string {
  53. return fmt.Sprintf("oss: service returned error: StatusCode=%d, ErrorCode=%s, ErrorMessage=%s, RequestId=%s",
  54. e.StatusCode, e.Code, e.Message, e.RequestId)
  55. }
  56. // NewClient New STS Client
  57. func NewClient(accessKeyId, accessKeySecret, roleArn, sessionName string) *Client {
  58. return &Client{
  59. AccessKeyId: accessKeyId,
  60. AccessKeySecret: accessKeySecret,
  61. RoleArn: roleArn,
  62. SessionName: sessionName,
  63. }
  64. }
  65. const (
  66. // StsSignVersion sts sign version
  67. StsSignVersion = "1.0"
  68. // StsAPIVersion sts api version
  69. StsAPIVersion = "2015-04-01"
  70. // StsHost sts host
  71. StsHost = "https://sts.aliyuncs.com/"
  72. // TimeFormat time fomrat
  73. TimeFormat = "2006-01-02T15:04:05Z"
  74. // RespBodyFormat respone body format
  75. RespBodyFormat = "JSON"
  76. // PercentEncode '/'
  77. PercentEncode = "%2F"
  78. // HTTPGet http get method
  79. HTTPGet = "GET"
  80. )
  81. // AssumeRole assume role
  82. func (c *Client) AssumeRole(expiredTime uint) (*Response, error) {
  83. url, err := c.generateSignedURL(expiredTime)
  84. if err != nil {
  85. return nil, err
  86. }
  87. body, status, err := c.sendRequest(url)
  88. if err != nil {
  89. return nil, err
  90. }
  91. return c.handleResponse(body, status)
  92. }
  93. // Private function
  94. func (c *Client) generateSignedURL(expiredTime uint) (string, error) {
  95. queryStr := "SignatureVersion=" + StsSignVersion
  96. queryStr += "&Format=" + RespBodyFormat
  97. queryStr += "&Timestamp=" + url.QueryEscape(time.Now().UTC().Format(TimeFormat))
  98. queryStr += "&RoleArn=" + url.QueryEscape(c.RoleArn)
  99. queryStr += "&RoleSessionName=" + c.SessionName
  100. queryStr += "&AccessKeyId=" + c.AccessKeyId
  101. queryStr += "&SignatureMethod=HMAC-SHA1"
  102. queryStr += "&Version=" + StsAPIVersion
  103. queryStr += "&Action=AssumeRole"
  104. uuid, _ := uuid.NewV4()
  105. queryStr += "&SignatureNonce=" + uuid.String()
  106. queryStr += "&DurationSeconds=" + strconv.FormatUint((uint64)(expiredTime), 10)
  107. // Sort query string
  108. queryParams, err := url.ParseQuery(queryStr)
  109. if err != nil {
  110. return "", err
  111. }
  112. result := queryParams.Encode()
  113. strToSign := HTTPGet + "&" + PercentEncode + "&" + url.QueryEscape(result)
  114. // Generate signature
  115. hashSign := hmac.New(sha1.New, []byte(c.AccessKeySecret+"&"))
  116. hashSign.Write([]byte(strToSign))
  117. signature := base64.StdEncoding.EncodeToString(hashSign.Sum(nil))
  118. // Build url
  119. assumeURL := StsHost + "?" + queryStr + "&Signature=" + url.QueryEscape(signature)
  120. return assumeURL, nil
  121. }
  122. func (c *Client) sendRequest(url string) ([]byte, int, error) {
  123. tr := &http.Transport{
  124. TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
  125. }
  126. client := &http.Client{Transport: tr}
  127. resp, err := client.Get(url)
  128. if err != nil {
  129. return nil, -1, err
  130. }
  131. defer resp.Body.Close()
  132. body, err := ioutil.ReadAll(resp.Body)
  133. return body, resp.StatusCode, err
  134. }
  135. func (c *Client) handleResponse(responseBody []byte, statusCode int) (*Response, error) {
  136. if statusCode != http.StatusOK {
  137. se := ServiceError{StatusCode: statusCode, RawMessage: string(responseBody)}
  138. err := json.Unmarshal(responseBody, &se)
  139. if err != nil {
  140. return nil, err
  141. }
  142. return nil, &se
  143. }
  144. resp := Response{}
  145. err := json.Unmarshal(responseBody, &resp)
  146. if err != nil {
  147. return nil, err
  148. }
  149. return &resp, nil
  150. }