You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
4.0 KiB

  1. package oauth
  2. import (
  3. "bytes"
  4. "fmt"
  5. "math"
  6. "net/http"
  7. "net/url"
  8. "strconv"
  9. "strings"
  10. )
  11. //
  12. // OAuth1 2-legged provider
  13. // Contributed by https://github.com/jacobpgallagher
  14. //
  15. // Provide an buffer reader which implements the Close() interface
  16. type oauthBufferReader struct {
  17. *bytes.Buffer
  18. }
  19. // So that it implements the io.ReadCloser interface
  20. func (m oauthBufferReader) Close() error { return nil }
  21. type ConsumerGetter func(key string, header map[string]string) (*Consumer, error)
  22. // Provider provides methods for a 2-legged Oauth1 provider
  23. type Provider struct {
  24. ConsumerGetter ConsumerGetter
  25. // For mocking
  26. clock clock
  27. }
  28. // NewProvider takes a function to get the consumer secret from a datastore.
  29. // Returns a Provider
  30. func NewProvider(secretGetter ConsumerGetter) *Provider {
  31. provider := &Provider{
  32. secretGetter,
  33. &defaultClock{},
  34. }
  35. return provider
  36. }
  37. // Combine a URL and Request to make the URL absolute
  38. func makeURLAbs(url *url.URL, request *http.Request) {
  39. if !url.IsAbs() {
  40. url.Host = request.Host
  41. if request.TLS != nil || request.Header.Get("X-Forwarded-Proto") == "https" {
  42. url.Scheme = "https"
  43. } else {
  44. url.Scheme = "http"
  45. }
  46. }
  47. }
  48. // IsAuthorized takes an *http.Request and returns a pointer to a string containing the consumer key,
  49. // or nil if not authorized
  50. func (provider *Provider) IsAuthorized(request *http.Request) (*string, error) {
  51. var err error
  52. var userParams map[string]string
  53. // start with the body/query params
  54. userParams, err = parseBody(request)
  55. if err != nil {
  56. return nil, err
  57. }
  58. // if the oauth params are in the Authorization header, grab them, and
  59. // let them override what's in userParams
  60. authHeader := request.Header.Get(HTTP_AUTH_HEADER)
  61. if len(authHeader) > 6 && strings.EqualFold(OAUTH_HEADER, authHeader[0:6]) {
  62. authHeader = authHeader[6:]
  63. params := strings.Split(authHeader, ",")
  64. for _, param := range params {
  65. vals := strings.SplitN(param, "=", 2)
  66. k := strings.Trim(vals[0], " ")
  67. v := strings.Trim(strings.Trim(vals[1], "\""), " ")
  68. if strings.HasPrefix(k, "oauth") {
  69. userParams[k], err = url.QueryUnescape(v)
  70. if err != nil {
  71. return nil, err
  72. }
  73. }
  74. }
  75. }
  76. // pop the request's signature, it's not included in our signature
  77. // calculation
  78. oauthSignature, ok := userParams[SIGNATURE_PARAM]
  79. if !ok {
  80. return nil, fmt.Errorf("no oauth signature")
  81. }
  82. delete(userParams, SIGNATURE_PARAM)
  83. // get the oauth consumer key
  84. consumerKey, ok := userParams[CONSUMER_KEY_PARAM]
  85. if !ok || consumerKey == "" {
  86. return nil, fmt.Errorf("no consumer key")
  87. }
  88. // use it to create a consumer object
  89. consumer, err := provider.ConsumerGetter(consumerKey, userParams)
  90. if err != nil {
  91. return nil, err
  92. }
  93. // Make sure timestamp is no more than 10 digits
  94. timestamp := userParams[TIMESTAMP_PARAM]
  95. if len(timestamp) > 10 {
  96. timestamp = timestamp[0:10]
  97. }
  98. // Check the timestamp
  99. if !consumer.serviceProvider.IgnoreTimestamp {
  100. oauthTimeNumber, err := strconv.Atoi(timestamp)
  101. if err != nil {
  102. return nil, err
  103. }
  104. if math.Abs(float64(int64(oauthTimeNumber)-provider.clock.Seconds())) > 5*60 {
  105. return nil, fmt.Errorf("too much clock skew")
  106. }
  107. }
  108. // Include the query string params in the base string
  109. if consumer.serviceProvider.SignQueryParams {
  110. for k, v := range request.URL.Query() {
  111. userParams[k] = strings.Join(v, "")
  112. }
  113. }
  114. // if our consumer supports bodyhash, check it
  115. if consumer.serviceProvider.BodyHash {
  116. bodyHash, err := calculateBodyHash(request, consumer.signer)
  117. if err != nil {
  118. return nil, err
  119. }
  120. sentHash, ok := userParams[BODY_HASH_PARAM]
  121. if bodyHash == "" && ok {
  122. return nil, fmt.Errorf("body_hash must not be set")
  123. } else if sentHash != bodyHash {
  124. return nil, fmt.Errorf("body_hash mismatch")
  125. }
  126. }
  127. allParams := NewOrderedParams()
  128. for key, value := range userParams {
  129. allParams.Add(key, value)
  130. }
  131. makeURLAbs(request.URL, request)
  132. baseString := consumer.requestString(request.Method, canonicalizeUrl(request.URL), allParams)
  133. err = consumer.signer.Verify(baseString, oauthSignature)
  134. if err != nil {
  135. return nil, err
  136. }
  137. return &consumerKey, nil
  138. }