mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
add functions/vendor files
This commit is contained in:
10
vendor/github.com/dghubble/oauth1/.travis.yml
generated
vendored
Normal file
10
vendor/github.com/dghubble/oauth1/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.7
|
||||
- 1.8
|
||||
- tip
|
||||
install:
|
||||
- go get github.com/golang/lint/golint
|
||||
- go get -v -t .
|
||||
script:
|
||||
- ./test
|
||||
30
vendor/github.com/dghubble/oauth1/CHANGES.md
generated
vendored
Normal file
30
vendor/github.com/dghubble/oauth1/CHANGES.md
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# OAuth1 Changelog
|
||||
|
||||
## v0.4.0 (2016-04-20)
|
||||
|
||||
* Add a Signer field to the Config to allow custom Signer implementations.
|
||||
* Use the HMACSigner by default. This provides the same signing behavior as in previous versions (HMAC-SHA1).
|
||||
* Add an RSASigner for "RSA-SHA1" OAuth1 Providers.
|
||||
* Add missing Authorization Header quotes around OAuth parameter values. Many providers allowed these quotes to be missing.
|
||||
* Change `Signer` to be a signer interface.
|
||||
* Remove the old Signer methods `SetAccessTokenAuthHeader`, `SetRequestAuthHeader`, and `SetRequestTokenAuthHeader`.
|
||||
|
||||
## v0.3.0 (2015-09-13)
|
||||
|
||||
* Added `NoContext` which may be used in most cases.
|
||||
* Allowed Transport Base http.RoundTripper to be set through a ctx.
|
||||
* Changed `NewClient` to require a context.Context.
|
||||
* Changed `Config.Client` to require a context.Context.
|
||||
|
||||
## v.0.2.0 (2015-08-30)
|
||||
|
||||
* Improved OAuth 1 spec compliance and test coverage.
|
||||
* Added `func StaticTokenSource(*Token) TokenSource`
|
||||
* Added `ParseAuthorizationCallback` function. Removed `Config.HandleAuthorizationCallback` method.
|
||||
* Changed `Config` method signatures to allow an interface to be defined for the OAuth1 authorization flow. Gives users of this package (and downstream packages) the freedom to use other implementations if they wish.
|
||||
* Removed `RequestToken` in favor of passing token and secret value strings.
|
||||
* Removed `ReuseTokenSource` struct, it was effectively a static source. Replaced by `StaticTokenSource`.
|
||||
|
||||
## v0.1.0 (2015-04-26)
|
||||
|
||||
* Initial OAuth1 support for obtaining authorization and making authorized requests.
|
||||
21
vendor/github.com/dghubble/oauth1/LICENSE
generated
vendored
Normal file
21
vendor/github.com/dghubble/oauth1/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Dalton Hubble
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
125
vendor/github.com/dghubble/oauth1/README.md
generated
vendored
Normal file
125
vendor/github.com/dghubble/oauth1/README.md
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
|
||||
# OAuth1 [](https://travis-ci.org/dghubble/oauth1) [](http://godoc.org/github.com/dghubble/oauth1)
|
||||
<img align="right" src="https://storage.googleapis.com/dghubble/oauth1.png">
|
||||
|
||||
OAauth1 is a Go implementation of the [OAuth 1 spec](https://tools.ietf.org/html/rfc5849).
|
||||
|
||||
It allows end-users to authorize a client (consumer) to access protected resources on his/her behalf and to make signed and authorized requests.
|
||||
|
||||
Package `oauth1` takes design cues from [golang.org/x/oauth2](https://godoc.org/golang.org/x/oauth2), to provide an analogous API and an `http.Client` with a Transport which signs/authorizes requests.
|
||||
|
||||
## Install
|
||||
|
||||
go get github.com/dghubble/oauth1
|
||||
|
||||
## Docs
|
||||
|
||||
Read [GoDoc](https://godoc.org/github.com/dghubble/oauth1)
|
||||
|
||||
## Usage
|
||||
|
||||
Package `oauth1` implements the OAuth1 authorization flow and provides an `http.Client` which can sign and authorize OAuth1 requests.
|
||||
|
||||
To implement "Login with X", use the [gologin](https://github.com/dghubble/gologin) packages which provide login handlers for OAuth1 and OAuth2 providers.
|
||||
|
||||
To call the Twitter, Digits, or Tumblr OAuth1 APIs, use the higher level Go API clients.
|
||||
|
||||
* [Twitter](https://github.com/dghubble/go-twitter)
|
||||
* [Digits](https://github.com/dghubble/go-digits)
|
||||
* [Tumblr](https://github.com/benfb/go-tumblr)
|
||||
|
||||
### Authorization Flow
|
||||
|
||||
Perform the OAuth 1 authorization flow to ask a user to grant an application access to his/her resources via an access token.
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/dghubble/oauth1"
|
||||
"github.com/dghubble/oauth1/twitter""
|
||||
)
|
||||
...
|
||||
|
||||
config := oauth1.Config{
|
||||
ConsumerKey: "consumerKey",
|
||||
ConsumerSecret: "consumerSecret",
|
||||
CallbackURL: "http://mysite.com/oauth/twitter/callback",
|
||||
Endpoint: twitter.AuthorizeEndpoint,
|
||||
}
|
||||
```
|
||||
|
||||
1. When a user performs an action (e.g. "Login with X" button calls "/login" route) get an OAuth1 request token (temporary credentials).
|
||||
|
||||
```go
|
||||
requestToken, requestSecret, err = config.RequestToken()
|
||||
// handle err
|
||||
```
|
||||
|
||||
2. Obtain authorization from the user by redirecting them to the OAuth1 provider's authorization URL to grant the application access.
|
||||
|
||||
```go
|
||||
authorizationURL, err := config.AuthorizationURL(requestToken)
|
||||
// handle err
|
||||
http.Redirect(w, req, authorizationURL.String(), http.StatusFound)
|
||||
```
|
||||
|
||||
Receive the callback from the OAuth1 provider in a handler.
|
||||
|
||||
```go
|
||||
requestToken, verifier, err := oauth1.ParseAuthorizationCallback(req)
|
||||
// handle err
|
||||
```
|
||||
|
||||
3. Acquire the access token (token credentials) which can later be used to make requests on behalf of the user.
|
||||
|
||||
```go
|
||||
accessToken, accessSecret, err := config.AccessToken(requestToken, requestSecret, verifier)
|
||||
// handle error
|
||||
token := oauth1.NewToken(accessToken, accessSecret)
|
||||
```
|
||||
|
||||
Check the [examples](examples) to see this authorization flow in action from the command line, with Twitter PIN-based login and Tumblr login.
|
||||
|
||||
### Authorized Requests
|
||||
|
||||
Use an access `Token` to make authorized requests on behalf of a user.
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/dghubble/oauth1"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config := oauth1.NewConfig("consumerKey", "consumerSecret")
|
||||
token := oauth1.NewToken("token", "tokenSecret")
|
||||
|
||||
// httpClient will automatically authorize http.Request's
|
||||
httpClient := config.Client(oauth1.NoContext, token)
|
||||
|
||||
// example Twitter API request
|
||||
path := "https://api.twitter.com/1.1/statuses/home_timeline.json?count=2"
|
||||
resp, _ := httpClient.Get(path)
|
||||
defer resp.Body.Close()
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Printf("Raw Response Body:\n%v\n", string(body))
|
||||
}
|
||||
```
|
||||
|
||||
Check the [examples](examples) to see Twitter and Tumblr requests in action.
|
||||
|
||||
### Concepts
|
||||
|
||||
An `Endpoint` groups an OAuth provider's token and authorization URL endpoints.Endpoints for common providers are provided in subpackages.
|
||||
|
||||
A `Config` stores a consumer application's consumer key and secret, the registered callback URL, and the `Endpoint` to which the consumer is registered. It provides OAuth1 authorization flow methods.
|
||||
|
||||
An OAuth1 `Token` is an access token which can be used to make signed requests on behalf of a user. See [Authorized Requests](#Authorized Requests) for details.
|
||||
|
||||
If you've used the [golang.org/x/oauth2](https://godoc.org/golang.org/x/oauth2) package for OAuth2 before, this organization should be familiar.
|
||||
|
||||
## Contributing
|
||||
|
||||
See the [Contributing Guide](https://gist.github.com/dghubble/be682c123727f70bcfe7).
|
||||
|
||||
## License
|
||||
|
||||
[MIT License](LICENSE)
|
||||
265
vendor/github.com/dghubble/oauth1/auther.go
generated
vendored
Normal file
265
vendor/github.com/dghubble/oauth1/auther.go
generated
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
authorizationHeaderParam = "Authorization"
|
||||
authorizationPrefix = "OAuth " // trailing space is intentional
|
||||
oauthConsumerKeyParam = "oauth_consumer_key"
|
||||
oauthNonceParam = "oauth_nonce"
|
||||
oauthSignatureParam = "oauth_signature"
|
||||
oauthSignatureMethodParam = "oauth_signature_method"
|
||||
oauthTimestampParam = "oauth_timestamp"
|
||||
oauthTokenParam = "oauth_token"
|
||||
oauthVersionParam = "oauth_version"
|
||||
oauthCallbackParam = "oauth_callback"
|
||||
oauthVerifierParam = "oauth_verifier"
|
||||
defaultOauthVersion = "1.0"
|
||||
contentType = "Content-Type"
|
||||
formContentType = "application/x-www-form-urlencoded"
|
||||
)
|
||||
|
||||
// clock provides a interface for current time providers. A Clock can be used
|
||||
// in place of calling time.Now() directly.
|
||||
type clock interface {
|
||||
Now() time.Time
|
||||
}
|
||||
|
||||
// A noncer provides random nonce strings.
|
||||
type noncer interface {
|
||||
Nonce() string
|
||||
}
|
||||
|
||||
// auther adds an "OAuth" Authorization header field to requests.
|
||||
type auther struct {
|
||||
config *Config
|
||||
clock clock
|
||||
noncer noncer
|
||||
}
|
||||
|
||||
func newAuther(config *Config) *auther {
|
||||
return &auther{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// setRequestTokenAuthHeader adds the OAuth1 header for the request token
|
||||
// request (temporary credential) according to RFC 5849 2.1.
|
||||
func (a *auther) setRequestTokenAuthHeader(req *http.Request) error {
|
||||
oauthParams := a.commonOAuthParams()
|
||||
oauthParams[oauthCallbackParam] = a.config.CallbackURL
|
||||
params, err := collectParameters(req, oauthParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signatureBase := signatureBase(req, params)
|
||||
signature, err := a.signer().Sign("", signatureBase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oauthParams[oauthSignatureParam] = signature
|
||||
req.Header.Set(authorizationHeaderParam, authHeaderValue(oauthParams))
|
||||
return nil
|
||||
}
|
||||
|
||||
// setAccessTokenAuthHeader sets the OAuth1 header for the access token request
|
||||
// (token credential) according to RFC 5849 2.3.
|
||||
func (a *auther) setAccessTokenAuthHeader(req *http.Request, requestToken, requestSecret, verifier string) error {
|
||||
oauthParams := a.commonOAuthParams()
|
||||
oauthParams[oauthTokenParam] = requestToken
|
||||
oauthParams[oauthVerifierParam] = verifier
|
||||
params, err := collectParameters(req, oauthParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signatureBase := signatureBase(req, params)
|
||||
signature, err := a.signer().Sign(requestSecret, signatureBase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oauthParams[oauthSignatureParam] = signature
|
||||
req.Header.Set(authorizationHeaderParam, authHeaderValue(oauthParams))
|
||||
return nil
|
||||
}
|
||||
|
||||
// setRequestAuthHeader sets the OAuth1 header for making authenticated
|
||||
// requests with an AccessToken (token credential) according to RFC 5849 3.1.
|
||||
func (a *auther) setRequestAuthHeader(req *http.Request, accessToken *Token) error {
|
||||
oauthParams := a.commonOAuthParams()
|
||||
oauthParams[oauthTokenParam] = accessToken.Token
|
||||
params, err := collectParameters(req, oauthParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signatureBase := signatureBase(req, params)
|
||||
signature, err := a.signer().Sign(accessToken.TokenSecret, signatureBase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oauthParams[oauthSignatureParam] = signature
|
||||
req.Header.Set(authorizationHeaderParam, authHeaderValue(oauthParams))
|
||||
return nil
|
||||
}
|
||||
|
||||
// commonOAuthParams returns a map of the common OAuth1 protocol parameters,
|
||||
// excluding the oauth_signature parameter.
|
||||
func (a *auther) commonOAuthParams() map[string]string {
|
||||
return map[string]string{
|
||||
oauthConsumerKeyParam: a.config.ConsumerKey,
|
||||
oauthSignatureMethodParam: a.signer().Name(),
|
||||
oauthTimestampParam: strconv.FormatInt(a.epoch(), 10),
|
||||
oauthNonceParam: a.nonce(),
|
||||
oauthVersionParam: defaultOauthVersion,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a base64 encoded random 32 byte string.
|
||||
func (a *auther) nonce() string {
|
||||
if a.noncer != nil {
|
||||
return a.noncer.Nonce()
|
||||
}
|
||||
b := make([]byte, 32)
|
||||
rand.Read(b)
|
||||
return base64.StdEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
// Returns the Unix epoch seconds.
|
||||
func (a *auther) epoch() int64 {
|
||||
if a.clock != nil {
|
||||
return a.clock.Now().Unix()
|
||||
}
|
||||
return time.Now().Unix()
|
||||
}
|
||||
|
||||
// Returns the Config's Signer or the default Signer.
|
||||
func (a *auther) signer() Signer {
|
||||
if a.config.Signer != nil {
|
||||
return a.config.Signer
|
||||
}
|
||||
return &HMACSigner{ConsumerSecret: a.config.ConsumerSecret}
|
||||
}
|
||||
|
||||
// authHeaderValue formats OAuth parameters according to RFC 5849 3.5.1. OAuth
|
||||
// params are percent encoded, sorted by key (for testability), and joined by
|
||||
// "=" into pairs. Pairs are joined with a ", " comma separator into a header
|
||||
// string.
|
||||
// The given OAuth params should include the "oauth_signature" key.
|
||||
func authHeaderValue(oauthParams map[string]string) string {
|
||||
pairs := sortParameters(encodeParameters(oauthParams), `%s="%s"`)
|
||||
return authorizationPrefix + strings.Join(pairs, ", ")
|
||||
}
|
||||
|
||||
// encodeParameters percent encodes parameter keys and values according to
|
||||
// RFC5849 3.6 and RFC3986 2.1 and returns a new map.
|
||||
func encodeParameters(params map[string]string) map[string]string {
|
||||
encoded := map[string]string{}
|
||||
for key, value := range params {
|
||||
encoded[PercentEncode(key)] = PercentEncode(value)
|
||||
}
|
||||
return encoded
|
||||
}
|
||||
|
||||
// sortParameters sorts parameters by key and returns a slice of key/value
|
||||
// pairs formatted with the given format string (e.g. "%s=%s").
|
||||
func sortParameters(params map[string]string, format string) []string {
|
||||
// sort by key
|
||||
keys := make([]string, len(params))
|
||||
i := 0
|
||||
for key := range params {
|
||||
keys[i] = key
|
||||
i++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
// parameter join
|
||||
pairs := make([]string, len(params))
|
||||
for i, key := range keys {
|
||||
pairs[i] = fmt.Sprintf(format, key, params[key])
|
||||
}
|
||||
return pairs
|
||||
}
|
||||
|
||||
// collectParameters collects request parameters from the request query, OAuth
|
||||
// parameters (which should exclude oauth_signature), and the request body
|
||||
// provided the body is single part, form encoded, and the form content type
|
||||
// header is set. The returned map of collected parameter keys and values
|
||||
// follow RFC 5849 3.4.1.3, except duplicate parameters are not supported.
|
||||
func collectParameters(req *http.Request, oauthParams map[string]string) (map[string]string, error) {
|
||||
// add oauth, query, and body parameters into params
|
||||
params := map[string]string{}
|
||||
for key, value := range req.URL.Query() {
|
||||
// most backends do not accept duplicate query keys
|
||||
params[key] = value[0]
|
||||
}
|
||||
if req.Body != nil && req.Header.Get(contentType) == formContentType {
|
||||
// reads data to a []byte, draining req.Body
|
||||
b, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values, err := url.ParseQuery(string(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for key, value := range values {
|
||||
// not supporting params with duplicate keys
|
||||
params[key] = value[0]
|
||||
}
|
||||
// reinitialize Body with ReadCloser over the []byte
|
||||
req.Body = ioutil.NopCloser(bytes.NewReader(b))
|
||||
}
|
||||
for key, value := range oauthParams {
|
||||
params[key] = value
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// signatureBase combines the uppercase request method, percent encoded base
|
||||
// string URI, and normalizes the request parameters int a parameter string.
|
||||
// Returns the OAuth1 signature base string according to RFC5849 3.4.1.
|
||||
func signatureBase(req *http.Request, params map[string]string) string {
|
||||
method := strings.ToUpper(req.Method)
|
||||
baseURL := baseURI(req)
|
||||
parameterString := normalizedParameterString(params)
|
||||
// signature base string constructed accoding to 3.4.1.1
|
||||
baseParts := []string{method, PercentEncode(baseURL), PercentEncode(parameterString)}
|
||||
return strings.Join(baseParts, "&")
|
||||
}
|
||||
|
||||
// baseURI returns the base string URI of a request according to RFC 5849
|
||||
// 3.4.1.2. The scheme and host are lowercased, the port is dropped if it
|
||||
// is 80 or 443, and the path minus query parameters is included.
|
||||
func baseURI(req *http.Request) string {
|
||||
scheme := strings.ToLower(req.URL.Scheme)
|
||||
host := strings.ToLower(req.URL.Host)
|
||||
if hostPort := strings.Split(host, ":"); len(hostPort) == 2 && (hostPort[1] == "80" || hostPort[1] == "443") {
|
||||
host = hostPort[0]
|
||||
}
|
||||
// TODO: use req.URL.EscapedPath() once Go 1.5 is more generally adopted
|
||||
// For now, hacky workaround accomplishes the same internal escaping mode
|
||||
// escape(u.Path, encodePath) for proper compliance with the OAuth1 spec.
|
||||
path := req.URL.Path
|
||||
if path != "" {
|
||||
path = strings.Split(req.URL.RequestURI(), "?")[0]
|
||||
}
|
||||
return fmt.Sprintf("%v://%v%v", scheme, host, path)
|
||||
}
|
||||
|
||||
// parameterString normalizes collected OAuth parameters (which should exclude
|
||||
// oauth_signature) into a parameter string as defined in RFC 5894 3.4.1.3.2.
|
||||
// The parameters are encoded, sorted by key, keys and values joined with "&",
|
||||
// and pairs joined with "=" (e.g. foo=bar&q=gopher).
|
||||
func normalizedParameterString(params map[string]string) string {
|
||||
return strings.Join(sortParameters(encodeParameters(params), "%s=%s"), "&")
|
||||
}
|
||||
244
vendor/github.com/dghubble/oauth1/auther_test.go
generated
vendored
Normal file
244
vendor/github.com/dghubble/oauth1/auther_test.go
generated
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCommonOAuthParams(t *testing.T) {
|
||||
config := &Config{ConsumerKey: "some_consumer_key"}
|
||||
auther := &auther{config, &fixedClock{time.Unix(50037133, 0)}, &fixedNoncer{"some_nonce"}}
|
||||
expectedParams := map[string]string{
|
||||
"oauth_consumer_key": "some_consumer_key",
|
||||
"oauth_signature_method": "HMAC-SHA1",
|
||||
"oauth_timestamp": "50037133",
|
||||
"oauth_nonce": "some_nonce",
|
||||
"oauth_version": "1.0",
|
||||
}
|
||||
assert.Equal(t, expectedParams, auther.commonOAuthParams())
|
||||
}
|
||||
|
||||
func TestNonce(t *testing.T) {
|
||||
auther := &auther{}
|
||||
nonce := auther.nonce()
|
||||
// assert that 32 bytes (256 bites) become 44 bytes since a base64 byte
|
||||
// zeros the 2 high bits. 3 bytes convert to 4 base64 bytes, 40 base64 bytes
|
||||
// represent the first 30 of 32 bytes, = padding adds another 4 byte group.
|
||||
// base64 bytes = 4 * floor(bytes/3) + 4
|
||||
assert.Equal(t, 44, len([]byte(nonce)))
|
||||
}
|
||||
|
||||
func TestEpoch(t *testing.T) {
|
||||
a := &auther{}
|
||||
// assert that a real time is used by default
|
||||
assert.InEpsilon(t, time.Now().Unix(), a.epoch(), 1)
|
||||
// assert that the fixed clock can be used for testing
|
||||
a = &auther{clock: &fixedClock{time.Unix(50037133, 0)}}
|
||||
assert.Equal(t, int64(50037133), a.epoch())
|
||||
}
|
||||
|
||||
func TestSigner_Default(t *testing.T) {
|
||||
config := &Config{ConsumerSecret: "consumer_secret"}
|
||||
a := newAuther(config)
|
||||
// echo -n "hello world" | openssl dgst -sha1 -hmac "consumer_secret&token_secret" -binary | base64
|
||||
expectedSignature := "BE0uILOruKfSXd4UzYlLJDfOq08="
|
||||
// assert that the default signer produces the expected HMAC-SHA1 digest
|
||||
method := a.signer().Name()
|
||||
digest, err := a.signer().Sign("token_secret", "hello world")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "HMAC-SHA1", method)
|
||||
assert.Equal(t, expectedSignature, digest)
|
||||
}
|
||||
|
||||
type identitySigner struct{}
|
||||
|
||||
func (s *identitySigner) Name() string {
|
||||
return "identity"
|
||||
}
|
||||
|
||||
func (s *identitySigner) Sign(tokenSecret, message string) (string, error) {
|
||||
return message, nil
|
||||
}
|
||||
|
||||
func TestSigner_Custom(t *testing.T) {
|
||||
config := &Config{
|
||||
ConsumerSecret: "consumer_secret",
|
||||
Signer: &identitySigner{},
|
||||
}
|
||||
a := newAuther(config)
|
||||
// assert that the custom signer is used
|
||||
method := a.signer().Name()
|
||||
digest, err := a.signer().Sign("secret", "hello world")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "identity", method)
|
||||
assert.Equal(t, "hello world", digest)
|
||||
}
|
||||
|
||||
func TestAuthHeaderValue(t *testing.T) {
|
||||
cases := []struct {
|
||||
params map[string]string
|
||||
authHeader string
|
||||
}{
|
||||
{map[string]string{}, "OAuth "},
|
||||
{map[string]string{"a": "b"}, `OAuth a="b"`},
|
||||
{map[string]string{"a": "b", "c": "d", "e": "f", "1": "2"}, `OAuth 1="2", a="b", c="d", e="f"`},
|
||||
{map[string]string{"/= +doencode": "/= +doencode"}, `OAuth %2F%3D%20%2Bdoencode="%2F%3D%20%2Bdoencode"`},
|
||||
{map[string]string{"-._~dontencode": "-._~dontencode"}, `OAuth -._~dontencode="-._~dontencode"`},
|
||||
}
|
||||
for _, c := range cases {
|
||||
assert.Equal(t, c.authHeader, authHeaderValue(c.params))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeParameters(t *testing.T) {
|
||||
input := map[string]string{
|
||||
"a": "Dogs, Cats & Mice",
|
||||
"☃": "snowman",
|
||||
"ル": "ル",
|
||||
}
|
||||
expected := map[string]string{
|
||||
"a": "Dogs%2C%20Cats%20%26%20Mice",
|
||||
"%E2%98%83": "snowman",
|
||||
"%E3%83%AB": "%E3%83%AB",
|
||||
}
|
||||
assert.Equal(t, expected, encodeParameters(input))
|
||||
}
|
||||
|
||||
func TestSortParameters(t *testing.T) {
|
||||
input := map[string]string{
|
||||
".": "ape",
|
||||
"5.6": "bat",
|
||||
"rsa": "cat",
|
||||
"%20": "dog",
|
||||
"%E3%83%AB": "eel",
|
||||
"dup": "fox",
|
||||
//"dup": "fix", // duplicate keys not supported
|
||||
}
|
||||
expected := []string{
|
||||
"%20=dog",
|
||||
"%E3%83%AB=eel",
|
||||
".=ape",
|
||||
"5.6=bat",
|
||||
"dup=fox",
|
||||
"rsa=cat",
|
||||
}
|
||||
assert.Equal(t, expected, sortParameters(input, "%s=%s"))
|
||||
}
|
||||
|
||||
func TestCollectParameters(t *testing.T) {
|
||||
// example from RFC 5849 3.4.1.3.1
|
||||
oauthParams := map[string]string{
|
||||
"oauth_token": "kkk9d7dh3k39sjv7",
|
||||
"oauth_consumer_key": "9djdj82h48djs9d2",
|
||||
"oauth_signature_method": "HMAC-SHA1",
|
||||
"oauth_timestamp": "137131201",
|
||||
"oauth_nonce": "7d8f3e4a",
|
||||
}
|
||||
values := url.Values{}
|
||||
values.Add("c2", "")
|
||||
values.Add("plus", "2 q") // duplicate keys not supported, a3 -> plus
|
||||
req, err := http.NewRequest("POST", "/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b", strings.NewReader(values.Encode()))
|
||||
assert.Nil(t, err)
|
||||
req.Header.Set(contentType, formContentType)
|
||||
params, err := collectParameters(req, oauthParams)
|
||||
// assert parameters were collected from oauthParams, the query, and form body
|
||||
expected := map[string]string{
|
||||
"b5": "=%3D",
|
||||
"a3": "a",
|
||||
"c@": "",
|
||||
"a2": "r b",
|
||||
"oauth_token": "kkk9d7dh3k39sjv7",
|
||||
"oauth_consumer_key": "9djdj82h48djs9d2",
|
||||
"oauth_signature_method": "HMAC-SHA1",
|
||||
"oauth_timestamp": "137131201",
|
||||
"oauth_nonce": "7d8f3e4a",
|
||||
"c2": "",
|
||||
"plus": "2 q",
|
||||
}
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expected, params)
|
||||
// RFC 5849 3.4.1.3.1 requires a {"a3"="2 q"} be form encoded to "a3=2+q" in
|
||||
// the application/x-www-form-urlencoded body. The parameter "2+q" should be
|
||||
// read as "2 q" and percent encoded to "2%20q".
|
||||
// In Go, data is form encoded by calling Encode on url.Values{} (URL
|
||||
// encoding) and decoded with url.ParseQuery to url.Values. So the encoding
|
||||
// of "2 q" to "2+q" and decoding back to "2 q" is handled and then params
|
||||
// are percent encoded.
|
||||
// http://golang.org/src/net/http/client.go#L496
|
||||
// http://golang.org/src/net/http/request.go#L837
|
||||
}
|
||||
|
||||
func TestSignatureBase(t *testing.T) {
|
||||
reqA, err := http.NewRequest("get", "HTTPS://HELLO.IO?q=test", nil)
|
||||
assert.Nil(t, err)
|
||||
reqB, err := http.NewRequest("POST", "http://hello.io:8080", nil)
|
||||
assert.Nil(t, err)
|
||||
cases := []struct {
|
||||
req *http.Request
|
||||
params map[string]string
|
||||
signatureBase string
|
||||
}{
|
||||
{reqA, map[string]string{"a": "b", "c": "d"}, "GET&https%3A%2F%2Fhello.io&a%3Db%26c%3Dd"},
|
||||
{reqB, map[string]string{"a": "b"}, "POST&http%3A%2F%2Fhello.io%3A8080&a%3Db"},
|
||||
}
|
||||
// assert that method is uppercased, base uri rules applied, queries added, joined by &
|
||||
for _, c := range cases {
|
||||
base := signatureBase(c.req, c.params)
|
||||
assert.Equal(t, c.signatureBase, base)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBaseURI(t *testing.T) {
|
||||
reqA, err := http.NewRequest("GET", "HTTP://EXAMPLE.COM:80/r%20v/X?id=123", nil)
|
||||
assert.Nil(t, err)
|
||||
reqB, err := http.NewRequest("POST", "https://www.example.net:8080/?q=1", nil)
|
||||
assert.Nil(t, err)
|
||||
reqC, err := http.NewRequest("POST", "https://example.com:443", nil)
|
||||
cases := []struct {
|
||||
req *http.Request
|
||||
baseURI string
|
||||
}{
|
||||
{reqA, "http://example.com/r%20v/X"},
|
||||
{reqB, "https://www.example.net:8080/"},
|
||||
{reqC, "https://example.com"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
baseURI := baseURI(c.req)
|
||||
assert.Equal(t, c.baseURI, baseURI)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizedParameterString(t *testing.T) {
|
||||
simple := map[string]string{
|
||||
"a": "b & c",
|
||||
"☃": "snowman",
|
||||
}
|
||||
rfcExample := map[string]string{
|
||||
"b5": "=%3D",
|
||||
"a3": "a",
|
||||
"c@": "",
|
||||
"a2": "r b",
|
||||
"oauth_token": "kkk9d7dh3k39sjv7",
|
||||
"oauth_consumer_key": "9djdj82h48djs9d2",
|
||||
"oauth_signature_method": "HMAC-SHA1",
|
||||
"oauth_timestamp": "137131201",
|
||||
"oauth_nonce": "7d8f3e4a",
|
||||
"c2": "",
|
||||
"plus": "2 q",
|
||||
}
|
||||
cases := []struct {
|
||||
params map[string]string
|
||||
parameterStr string
|
||||
}{
|
||||
{simple, "%E2%98%83=snowman&a=b%20%26%20c"},
|
||||
{rfcExample, "a2=r%20b&a3=a&b5=%3D%253D&c%40=&c2=&oauth_consumer_key=9djdj82h48djs9d2&oauth_nonce=7d8f3e4a&oauth_signature_method=HMAC-SHA1&oauth_timestamp=137131201&oauth_token=kkk9d7dh3k39sjv7&plus=2%20q"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
assert.Equal(t, c.parameterStr, normalizedParameterString(c.params))
|
||||
}
|
||||
}
|
||||
172
vendor/github.com/dghubble/oauth1/config.go
generated
vendored
Normal file
172
vendor/github.com/dghubble/oauth1/config.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
const (
|
||||
oauthTokenSecretParam = "oauth_token_secret"
|
||||
oauthCallbackConfirmedParam = "oauth_callback_confirmed"
|
||||
)
|
||||
|
||||
// Config represents an OAuth1 consumer's (client's) key and secret, the
|
||||
// callback URL, and the provider Endpoint to which the consumer corresponds.
|
||||
type Config struct {
|
||||
// Consumer Key (Client Identifier)
|
||||
ConsumerKey string
|
||||
// Consumer Secret (Client Shared-Secret)
|
||||
ConsumerSecret string
|
||||
// Callback URL
|
||||
CallbackURL string
|
||||
// Provider Endpoint specifying OAuth1 endpoint URLs
|
||||
Endpoint Endpoint
|
||||
// OAuth1 Signer (defaults to HMAC-SHA1)
|
||||
Signer Signer
|
||||
}
|
||||
|
||||
// NewConfig returns a new Config with the given consumer key and secret.
|
||||
func NewConfig(consumerKey, consumerSecret string) *Config {
|
||||
return &Config{
|
||||
ConsumerKey: consumerKey,
|
||||
ConsumerSecret: consumerSecret,
|
||||
}
|
||||
}
|
||||
|
||||
// Client returns an HTTP client which uses the provided ctx and access Token.
|
||||
func (c *Config) Client(ctx context.Context, t *Token) *http.Client {
|
||||
return NewClient(ctx, c, t)
|
||||
}
|
||||
|
||||
// NewClient returns a new http Client which signs requests via OAuth1.
|
||||
func NewClient(ctx context.Context, config *Config, token *Token) *http.Client {
|
||||
transport := &Transport{
|
||||
Base: contextTransport(ctx),
|
||||
source: StaticTokenSource(token),
|
||||
auther: newAuther(config),
|
||||
}
|
||||
return &http.Client{Transport: transport}
|
||||
}
|
||||
|
||||
// RequestToken obtains a Request token and secret (temporary credential) by
|
||||
// POSTing a request (with oauth_callback in the auth header) to the Endpoint
|
||||
// RequestTokenURL. The response body form is validated to ensure
|
||||
// oauth_callback_confirmed is true. Returns the request token and secret
|
||||
// (temporary credentials).
|
||||
// See RFC 5849 2.1 Temporary Credentials.
|
||||
func (c *Config) RequestToken() (requestToken, requestSecret string, err error) {
|
||||
req, err := http.NewRequest("POST", c.Endpoint.RequestTokenURL, nil)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
err = newAuther(c).setRequestTokenAuthHeader(req)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
// when err is nil, resp contains a non-nil resp.Body which must be closed
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
|
||||
return "", "", fmt.Errorf("oauth1: Server returned status %d", resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
// ParseQuery to decode URL-encoded application/x-www-form-urlencoded body
|
||||
values, err := url.ParseQuery(string(body))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
requestToken = values.Get(oauthTokenParam)
|
||||
requestSecret = values.Get(oauthTokenSecretParam)
|
||||
if requestToken == "" || requestSecret == "" {
|
||||
return "", "", errors.New("oauth1: Response missing oauth_token or oauth_token_secret")
|
||||
}
|
||||
if values.Get(oauthCallbackConfirmedParam) != "true" {
|
||||
return "", "", errors.New("oauth1: oauth_callback_confirmed was not true")
|
||||
}
|
||||
return requestToken, requestSecret, nil
|
||||
}
|
||||
|
||||
// AuthorizationURL accepts a request token and returns the *url.URL to the
|
||||
// Endpoint's authorization page that asks the user (resource owner) for to
|
||||
// authorize the consumer to act on his/her/its behalf.
|
||||
// See RFC 5849 2.2 Resource Owner Authorization.
|
||||
func (c *Config) AuthorizationURL(requestToken string) (*url.URL, error) {
|
||||
authorizationURL, err := url.Parse(c.Endpoint.AuthorizeURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values := authorizationURL.Query()
|
||||
values.Add(oauthTokenParam, requestToken)
|
||||
authorizationURL.RawQuery = values.Encode()
|
||||
return authorizationURL, nil
|
||||
}
|
||||
|
||||
// ParseAuthorizationCallback parses an OAuth1 authorization callback request
|
||||
// from a provider server. The oauth_token and oauth_verifier parameters are
|
||||
// parsed to return the request token from earlier in the flow and the
|
||||
// verifier string.
|
||||
// See RFC 5849 2.2 Resource Owner Authorization.
|
||||
func ParseAuthorizationCallback(req *http.Request) (requestToken, verifier string, err error) {
|
||||
// parse the raw query from the URL into req.Form
|
||||
err = req.ParseForm()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
requestToken = req.Form.Get(oauthTokenParam)
|
||||
verifier = req.Form.Get(oauthVerifierParam)
|
||||
if requestToken == "" || verifier == "" {
|
||||
return "", "", errors.New("oauth1: Request missing oauth_token or oauth_verifier")
|
||||
}
|
||||
return requestToken, verifier, nil
|
||||
}
|
||||
|
||||
// AccessToken obtains an access token (token credential) by POSTing a
|
||||
// request (with oauth_token and oauth_verifier in the auth header) to the
|
||||
// Endpoint AccessTokenURL. Returns the access token and secret (token
|
||||
// credentials).
|
||||
// See RFC 5849 2.3 Token Credentials.
|
||||
func (c *Config) AccessToken(requestToken, requestSecret, verifier string) (accessToken, accessSecret string, err error) {
|
||||
req, err := http.NewRequest("POST", c.Endpoint.AccessTokenURL, nil)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
err = newAuther(c).setAccessTokenAuthHeader(req, requestToken, requestSecret, verifier)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
// when err is nil, resp contains a non-nil resp.Body which must be closed
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
|
||||
return "", "", fmt.Errorf("oauth1: Server returned status %d", resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
// ParseQuery to decode URL-encoded application/x-www-form-urlencoded body
|
||||
values, err := url.ParseQuery(string(body))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
accessToken = values.Get(oauthTokenParam)
|
||||
accessSecret = values.Get(oauthTokenSecretParam)
|
||||
if accessToken == "" || accessSecret == "" {
|
||||
return "", "", errors.New("oauth1: Response missing oauth_token or oauth_token_secret")
|
||||
}
|
||||
return accessToken, accessSecret, nil
|
||||
}
|
||||
346
vendor/github.com/dghubble/oauth1/config_test.go
generated
vendored
Normal file
346
vendor/github.com/dghubble/oauth1/config_test.go
generated
vendored
Normal file
@@ -0,0 +1,346 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
const expectedVerifier = "some_verifier"
|
||||
|
||||
func TestNewConfig(t *testing.T) {
|
||||
expectedConsumerKey := "consumer_key"
|
||||
expectedConsumerSecret := "consumer_secret"
|
||||
config := NewConfig(expectedConsumerKey, expectedConsumerSecret)
|
||||
assert.Equal(t, expectedConsumerKey, config.ConsumerKey)
|
||||
assert.Equal(t, expectedConsumerSecret, config.ConsumerSecret)
|
||||
}
|
||||
|
||||
func TestNewClient(t *testing.T) {
|
||||
expectedToken := "access_token"
|
||||
expectedConsumerKey := "consumer_key"
|
||||
config := NewConfig(expectedConsumerKey, "consumer_secret")
|
||||
token := NewToken(expectedToken, "access_secret")
|
||||
client := config.Client(NoContext, token)
|
||||
|
||||
server := newMockServer(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equal(t, "GET", req.Method)
|
||||
params := parseOAuthParamsOrFail(t, req.Header.Get(authorizationHeaderParam))
|
||||
assert.Equal(t, expectedToken, params[oauthTokenParam])
|
||||
assert.Equal(t, expectedConsumerKey, params[oauthConsumerKeyParam])
|
||||
})
|
||||
defer server.Close()
|
||||
client.Get(server.URL)
|
||||
}
|
||||
|
||||
func TestNewClient_DefaultTransport(t *testing.T) {
|
||||
client := NewClient(NoContext, NewConfig("t", "s"), NewToken("t", "s"))
|
||||
// assert that the client uses the DefaultTransport
|
||||
transport, ok := client.Transport.(*Transport)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, http.DefaultTransport, transport.base())
|
||||
}
|
||||
|
||||
func TestNewClient_ContextClientTransport(t *testing.T) {
|
||||
baseTransport := &http.Transport{}
|
||||
baseClient := &http.Client{Transport: baseTransport}
|
||||
ctx := context.WithValue(NoContext, HTTPClient, baseClient)
|
||||
client := NewClient(ctx, NewConfig("t", "s"), NewToken("t", "s"))
|
||||
// assert that the client uses the ctx client's Transport as its base RoundTripper
|
||||
transport, ok := client.Transport.(*Transport)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, baseTransport, transport.base())
|
||||
}
|
||||
|
||||
// newRequestTokenServer returns a new httptest.Server for an OAuth1 provider
|
||||
// request token endpoint.
|
||||
func newRequestTokenServer(t *testing.T, data url.Values) *httptest.Server {
|
||||
return newMockServer(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equal(t, "POST", req.Method)
|
||||
assert.NotEmpty(t, req.Header.Get("Authorization"))
|
||||
w.Header().Set(contentType, formContentType)
|
||||
w.Write([]byte(data.Encode()))
|
||||
})
|
||||
}
|
||||
|
||||
// newAccessTokenServer returns a new httptest.Server for an OAuth1 provider
|
||||
// access token endpoint.
|
||||
func newAccessTokenServer(t *testing.T, data url.Values) *httptest.Server {
|
||||
return newMockServer(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equal(t, "POST", req.Method)
|
||||
assert.NotEmpty(t, req.Header.Get("Authorization"))
|
||||
params := parseOAuthParamsOrFail(t, req.Header.Get(authorizationHeaderParam))
|
||||
assert.Equal(t, expectedVerifier, params[oauthVerifierParam])
|
||||
w.Header().Set(contentType, formContentType)
|
||||
w.Write([]byte(data.Encode()))
|
||||
})
|
||||
}
|
||||
|
||||
// newUnparseableBodyServer returns a new httptest.Server which writes
|
||||
// responses with bodies that error when parsed by url.ParseQuery.
|
||||
func newUnparseableBodyServer() *httptest.Server {
|
||||
return newMockServer(func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Set(contentType, formContentType)
|
||||
// url.ParseQuery will error, https://golang.org/src/net/url/url_test.go#L1107
|
||||
w.Write([]byte("%gh&%ij"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestConfigRequestToken(t *testing.T) {
|
||||
expectedToken := "reqest_token"
|
||||
expectedSecret := "request_secret"
|
||||
data := url.Values{}
|
||||
data.Add("oauth_token", expectedToken)
|
||||
data.Add("oauth_token_secret", expectedSecret)
|
||||
data.Add("oauth_callback_confirmed", "true")
|
||||
server := newRequestTokenServer(t, data)
|
||||
defer server.Close()
|
||||
|
||||
config := &Config{
|
||||
Endpoint: Endpoint{
|
||||
RequestTokenURL: server.URL,
|
||||
},
|
||||
}
|
||||
requestToken, requestSecret, err := config.RequestToken()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expectedToken, requestToken)
|
||||
assert.Equal(t, expectedSecret, requestSecret)
|
||||
}
|
||||
|
||||
func TestConfigRequestToken_InvalidRequestTokenURL(t *testing.T) {
|
||||
config := &Config{
|
||||
Endpoint: Endpoint{
|
||||
RequestTokenURL: "http://wrong.com/oauth/request_token",
|
||||
},
|
||||
}
|
||||
requestToken, requestSecret, err := config.RequestToken()
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "", requestToken)
|
||||
assert.Equal(t, "", requestSecret)
|
||||
}
|
||||
|
||||
func TestConfigRequestToken_CallbackNotConfirmed(t *testing.T) {
|
||||
const expectedToken = "reqest_token"
|
||||
const expectedSecret = "request_secret"
|
||||
data := url.Values{}
|
||||
data.Add("oauth_token", expectedToken)
|
||||
data.Add("oauth_token_secret", expectedSecret)
|
||||
data.Add("oauth_callback_confirmed", "false")
|
||||
server := newRequestTokenServer(t, data)
|
||||
defer server.Close()
|
||||
|
||||
config := &Config{
|
||||
Endpoint: Endpoint{
|
||||
RequestTokenURL: server.URL,
|
||||
},
|
||||
}
|
||||
requestToken, requestSecret, err := config.RequestToken()
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, "oauth1: oauth_callback_confirmed was not true", err.Error())
|
||||
}
|
||||
assert.Equal(t, "", requestToken)
|
||||
assert.Equal(t, "", requestSecret)
|
||||
}
|
||||
|
||||
func TestConfigRequestToken_CannotParseBody(t *testing.T) {
|
||||
server := newUnparseableBodyServer()
|
||||
defer server.Close()
|
||||
|
||||
config := &Config{
|
||||
Endpoint: Endpoint{
|
||||
RequestTokenURL: server.URL,
|
||||
},
|
||||
}
|
||||
requestToken, requestSecret, err := config.RequestToken()
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "invalid URL escape")
|
||||
}
|
||||
assert.Equal(t, "", requestToken)
|
||||
assert.Equal(t, "", requestSecret)
|
||||
}
|
||||
|
||||
func TestConfigRequestToken_MissingTokenOrSecret(t *testing.T) {
|
||||
data := url.Values{}
|
||||
data.Add("oauth_token", "any_token")
|
||||
data.Add("oauth_callback_confirmed", "true")
|
||||
server := newRequestTokenServer(t, data)
|
||||
defer server.Close()
|
||||
|
||||
config := &Config{
|
||||
Endpoint: Endpoint{
|
||||
RequestTokenURL: server.URL,
|
||||
},
|
||||
}
|
||||
requestToken, requestSecret, err := config.RequestToken()
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, "oauth1: Response missing oauth_token or oauth_token_secret", err.Error())
|
||||
}
|
||||
assert.Equal(t, "", requestToken)
|
||||
assert.Equal(t, "", requestSecret)
|
||||
}
|
||||
|
||||
func TestAuthorizationURL(t *testing.T) {
|
||||
expectedURL := "https://api.example.com/oauth/authorize?oauth_token=a%2Frequest_token"
|
||||
config := &Config{
|
||||
Endpoint: Endpoint{
|
||||
AuthorizeURL: "https://api.example.com/oauth/authorize",
|
||||
},
|
||||
}
|
||||
url, err := config.AuthorizationURL("a/request_token")
|
||||
assert.Nil(t, err)
|
||||
if assert.NotNil(t, url) {
|
||||
assert.Equal(t, expectedURL, url.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorizationURL_CannotParseAuthorizeURL(t *testing.T) {
|
||||
config := &Config{
|
||||
Endpoint: Endpoint{
|
||||
AuthorizeURL: "%gh&%ij",
|
||||
},
|
||||
}
|
||||
url, err := config.AuthorizationURL("any_request_token")
|
||||
assert.Nil(t, url)
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "parse")
|
||||
assert.Contains(t, err.Error(), "invalid URL")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigAccessToken(t *testing.T) {
|
||||
expectedToken := "access_token"
|
||||
expectedSecret := "access_secret"
|
||||
data := url.Values{}
|
||||
data.Add("oauth_token", expectedToken)
|
||||
data.Add("oauth_token_secret", expectedSecret)
|
||||
server := newAccessTokenServer(t, data)
|
||||
defer server.Close()
|
||||
|
||||
config := &Config{
|
||||
Endpoint: Endpoint{
|
||||
AccessTokenURL: server.URL,
|
||||
},
|
||||
}
|
||||
accessToken, accessSecret, err := config.AccessToken("request_token", "request_secret", expectedVerifier)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expectedToken, accessToken)
|
||||
assert.Equal(t, expectedSecret, accessSecret)
|
||||
}
|
||||
|
||||
func TestConfigAccessToken_InvalidAccessTokenURL(t *testing.T) {
|
||||
config := &Config{
|
||||
Endpoint: Endpoint{
|
||||
AccessTokenURL: "http://wrong.com/oauth/access_token",
|
||||
},
|
||||
}
|
||||
accessToken, accessSecret, err := config.AccessToken("any_token", "any_secret", "any_verifier")
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "", accessToken)
|
||||
assert.Equal(t, "", accessSecret)
|
||||
}
|
||||
|
||||
func TestConfigAccessToken_CannotParseBody(t *testing.T) {
|
||||
server := newUnparseableBodyServer()
|
||||
defer server.Close()
|
||||
|
||||
config := &Config{
|
||||
Endpoint: Endpoint{
|
||||
AccessTokenURL: server.URL,
|
||||
},
|
||||
}
|
||||
accessToken, accessSecret, err := config.AccessToken("any_token", "any_secret", "any_verifier")
|
||||
if assert.Error(t, err) {
|
||||
assert.Contains(t, err.Error(), "invalid URL escape")
|
||||
}
|
||||
assert.Equal(t, "", accessToken)
|
||||
assert.Equal(t, "", accessSecret)
|
||||
}
|
||||
|
||||
func TestConfigAccessToken_MissingTokenOrSecret(t *testing.T) {
|
||||
data := url.Values{}
|
||||
data.Add("oauth_token", "any_token")
|
||||
server := newAccessTokenServer(t, data)
|
||||
defer server.Close()
|
||||
|
||||
config := &Config{
|
||||
Endpoint: Endpoint{
|
||||
AccessTokenURL: server.URL,
|
||||
},
|
||||
}
|
||||
accessToken, accessSecret, err := config.AccessToken("request_token", "request_secret", expectedVerifier)
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, "oauth1: Response missing oauth_token or oauth_token_secret", err.Error())
|
||||
}
|
||||
assert.Equal(t, "", accessToken)
|
||||
assert.Equal(t, "", accessSecret)
|
||||
}
|
||||
|
||||
func TestParseAuthorizationCallback_GET(t *testing.T) {
|
||||
expectedToken := "token"
|
||||
expectedVerifier := "verifier"
|
||||
server := newMockServer(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equal(t, "GET", req.Method)
|
||||
// logic under test
|
||||
requestToken, verifier, err := ParseAuthorizationCallback(req)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expectedToken, requestToken)
|
||||
assert.Equal(t, expectedVerifier, verifier)
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
// OAuth1 provider calls callback url
|
||||
url, err := url.Parse(server.URL)
|
||||
assert.Nil(t, err)
|
||||
query := url.Query()
|
||||
query.Add("oauth_token", expectedToken)
|
||||
query.Add("oauth_verifier", expectedVerifier)
|
||||
url.RawQuery = query.Encode()
|
||||
http.Get(url.String())
|
||||
}
|
||||
|
||||
func TestParseAuthorizationCallback_POST(t *testing.T) {
|
||||
expectedToken := "token"
|
||||
expectedVerifier := "verifier"
|
||||
server := newMockServer(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equal(t, "POST", req.Method)
|
||||
// logic under test
|
||||
requestToken, verifier, err := ParseAuthorizationCallback(req)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expectedToken, requestToken)
|
||||
assert.Equal(t, expectedVerifier, verifier)
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
// OAuth1 provider calls callback url
|
||||
form := url.Values{}
|
||||
form.Add("oauth_token", expectedToken)
|
||||
form.Add("oauth_verifier", expectedVerifier)
|
||||
http.PostForm(server.URL, form)
|
||||
}
|
||||
|
||||
func TestParseAuthorizationCallback_MissingTokenOrVerifier(t *testing.T) {
|
||||
server := newMockServer(func(w http.ResponseWriter, req *http.Request) {
|
||||
assert.Equal(t, "GET", req.Method)
|
||||
// logic under test
|
||||
requestToken, verifier, err := ParseAuthorizationCallback(req)
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, "oauth1: Request missing oauth_token or oauth_verifier", err.Error())
|
||||
}
|
||||
assert.Equal(t, "", requestToken)
|
||||
assert.Equal(t, "", verifier)
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
// OAuth1 provider calls callback url
|
||||
url, err := url.Parse(server.URL)
|
||||
assert.Nil(t, err)
|
||||
query := url.Query()
|
||||
query.Add("oauth_token", "any_token")
|
||||
query.Add("oauth_verifier", "") // missing oauth_verifier
|
||||
url.RawQuery = query.Encode()
|
||||
http.Get(url.String())
|
||||
}
|
||||
24
vendor/github.com/dghubble/oauth1/context.go
generated
vendored
Normal file
24
vendor/github.com/dghubble/oauth1/context.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type contextKey struct{}
|
||||
|
||||
// HTTPClient is the context key to associate an *http.Client value with
|
||||
// a context.
|
||||
var HTTPClient contextKey
|
||||
|
||||
// NoContext is the default context to use in most cases.
|
||||
var NoContext = context.TODO()
|
||||
|
||||
// contextTransport gets the Transport from the context client or nil.
|
||||
func contextTransport(ctx context.Context) http.RoundTripper {
|
||||
if client, ok := ctx.Value(HTTPClient).(*http.Client); ok {
|
||||
return client.Transport
|
||||
}
|
||||
return nil
|
||||
}
|
||||
21
vendor/github.com/dghubble/oauth1/context_test.go
generated
vendored
Normal file
21
vendor/github.com/dghubble/oauth1/context_test.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestContextTransport(t *testing.T) {
|
||||
client := &http.Client{
|
||||
Transport: http.DefaultTransport,
|
||||
}
|
||||
ctx := context.WithValue(NoContext, HTTPClient, client)
|
||||
assert.Equal(t, http.DefaultTransport, contextTransport(ctx))
|
||||
}
|
||||
|
||||
func TestContextTransport_NoContextClient(t *testing.T) {
|
||||
assert.Nil(t, contextTransport(NoContext))
|
||||
}
|
||||
97
vendor/github.com/dghubble/oauth1/doc.go
generated
vendored
Normal file
97
vendor/github.com/dghubble/oauth1/doc.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
Package oauth1 is a Go implementation of the OAuth1 spec RFC 5849.
|
||||
|
||||
It allows end-users to authorize a client (consumer) to access protected
|
||||
resources on their behalf (e.g. login) and allows clients to make signed and
|
||||
authorized requests on behalf of a user (e.g. API calls).
|
||||
|
||||
It takes design cues from golang.org/x/oauth2, providing an http.Client which
|
||||
handles request signing and authorization.
|
||||
|
||||
Usage
|
||||
|
||||
Package oauth1 implements the OAuth1 authorization flow and provides an
|
||||
http.Client which can sign and authorize OAuth1 requests.
|
||||
|
||||
To implement "Login with X", use the https://github.com/dghubble/gologin
|
||||
packages which provide login handlers for OAuth1 and OAuth2 providers.
|
||||
|
||||
To call the Twitter, Digits, or Tumblr OAuth1 APIs, use the higher level Go API
|
||||
clients.
|
||||
|
||||
* https://github.com/dghubble/go-twitter
|
||||
* https://github.com/dghubble/go-digits
|
||||
* https://github.com/benfb/go-tumblr
|
||||
|
||||
Authorization Flow
|
||||
|
||||
Perform the OAuth 1 authorization flow to ask a user to grant an application
|
||||
access to his/her resources via an access token.
|
||||
|
||||
import (
|
||||
"github.com/dghubble/oauth1"
|
||||
"github.com/dghubble/oauth1/twitter""
|
||||
)
|
||||
...
|
||||
|
||||
config := oauth1.Config{
|
||||
ConsumerKey: "consumerKey",
|
||||
ConsumerSecret: "consumerSecret",
|
||||
CallbackURL: "http://mysite.com/oauth/twitter/callback",
|
||||
Endpoint: twitter.AuthorizeEndpoint,
|
||||
}
|
||||
|
||||
1. When a user performs an action (e.g. "Login with X" button calls "/login"
|
||||
route) get an OAuth1 request token (temporary credentials).
|
||||
|
||||
requestToken, requestSecret, err = config.RequestToken()
|
||||
// handle err
|
||||
|
||||
2. Obtain authorization from the user by redirecting them to the OAuth1
|
||||
provider's authorization URL to grant the application access.
|
||||
|
||||
authorizationURL, err := config.AuthorizationURL(requestToken)
|
||||
// handle err
|
||||
http.Redirect(w, req, authorizationURL.String(), htt.StatusFound)
|
||||
|
||||
Receive the callback from the OAuth1 provider in a handler.
|
||||
|
||||
requestToken, verifier, err := oauth1.ParseAuthorizationCallback(req)
|
||||
// handle err
|
||||
|
||||
3. Acquire the access token (token credentials) which can later be used
|
||||
to make requests on behalf of the user.
|
||||
|
||||
accessToken, accessSecret, err := config.AccessToken(requestToken, requestSecret, verifier)
|
||||
// handle error
|
||||
token := oauth1.NewToken(accessToken, accessSecret)
|
||||
|
||||
Check the examples to see this authorization flow in action from the command
|
||||
line, with Twitter PIN-based login and Tumblr login.
|
||||
|
||||
Authorized Requests
|
||||
|
||||
Use an access Token to make authorized requests on behalf of a user.
|
||||
|
||||
import (
|
||||
"github.com/dghubble/oauth1"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config := oauth1.NewConfig("consumerKey", "consumerSecret")
|
||||
token := oauth1.NewToken("token", "tokenSecret")
|
||||
|
||||
// httpClient will automatically authorize http.Request's
|
||||
httpClient := config.Client(token)
|
||||
|
||||
// example Twitter API request
|
||||
path := "https://api.twitter.com/1.1/statuses/home_timeline.json?count=2"
|
||||
resp, _ := httpClient.Get(path)
|
||||
defer resp.Body.Close()
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Printf("Raw Response Body:\n%v\n", string(body))
|
||||
}
|
||||
|
||||
Check the examples to see Twitter and Tumblr requests in action.
|
||||
*/
|
||||
package oauth1
|
||||
13
vendor/github.com/dghubble/oauth1/dropbox/dropbox.go
generated
vendored
Normal file
13
vendor/github.com/dghubble/oauth1/dropbox/dropbox.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Package dropbox provides constants for using OAuth1 to access Dropbox.
|
||||
package dropbox
|
||||
|
||||
import (
|
||||
"github.com/dghubble/oauth1"
|
||||
)
|
||||
|
||||
// Endpoint is Dropbox's OAuth 1 endpoint.
|
||||
var Endpoint = oauth1.Endpoint{
|
||||
RequestTokenURL: "https://api.dropbox.com/1/oauth/request_token",
|
||||
AuthorizeURL: "https://api.dropbox.com/1/oauth/authorize",
|
||||
AccessTokenURL: "https://api.dropbox.com/1/oauth/access_token",
|
||||
}
|
||||
36
vendor/github.com/dghubble/oauth1/encode.go
generated
vendored
Normal file
36
vendor/github.com/dghubble/oauth1/encode.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// PercentEncode percent encodes a string according to RFC 3986 2.1.
|
||||
func PercentEncode(input string) string {
|
||||
var buf bytes.Buffer
|
||||
for _, b := range []byte(input) {
|
||||
// if in unreserved set
|
||||
if shouldEscape(b) {
|
||||
buf.Write([]byte(fmt.Sprintf("%%%02X", b)))
|
||||
} else {
|
||||
// do not escape, write byte as-is
|
||||
buf.WriteByte(b)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// shouldEscape returns false if the byte is an unreserved character that
|
||||
// should not be escaped and true otherwise, according to RFC 3986 2.1.
|
||||
func shouldEscape(c byte) bool {
|
||||
// RFC3986 2.3 unreserved characters
|
||||
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
|
||||
return false
|
||||
}
|
||||
switch c {
|
||||
case '-', '.', '_', '~':
|
||||
return false
|
||||
}
|
||||
// all other bytes must be escaped
|
||||
return true
|
||||
}
|
||||
27
vendor/github.com/dghubble/oauth1/encode_test.go
generated
vendored
Normal file
27
vendor/github.com/dghubble/oauth1/encode_test.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPercentEncode(t *testing.T) {
|
||||
cases := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{" ", "%20"},
|
||||
{"%", "%25"},
|
||||
{"&", "%26"},
|
||||
{"-._", "-._"},
|
||||
{" /=+", "%20%2F%3D%2B"},
|
||||
{"Ladies + Gentlemen", "Ladies%20%2B%20Gentlemen"},
|
||||
{"An encoded string!", "An%20encoded%20string%21"},
|
||||
{"Dogs, Cats & Mice", "Dogs%2C%20Cats%20%26%20Mice"},
|
||||
{"☃", "%E2%98%83"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if output := PercentEncode(c.input); output != c.expected {
|
||||
t.Errorf("expected %s, got %s", c.expected, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
12
vendor/github.com/dghubble/oauth1/endpoint.go
generated
vendored
Normal file
12
vendor/github.com/dghubble/oauth1/endpoint.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package oauth1
|
||||
|
||||
// Endpoint represents an OAuth1 provider's (server's) request token,
|
||||
// owner authorization, and access token request URLs.
|
||||
type Endpoint struct {
|
||||
// Request URL (Temporary Credential Request URI)
|
||||
RequestTokenURL string
|
||||
// Authorize URL (Resource Owner Authorization URI)
|
||||
AuthorizeURL string
|
||||
// Access Token URL (Token Request URI)
|
||||
AccessTokenURL string
|
||||
}
|
||||
48
vendor/github.com/dghubble/oauth1/examples/README.md
generated
vendored
Normal file
48
vendor/github.com/dghubble/oauth1/examples/README.md
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
# OAuth1 Examples
|
||||
|
||||
## Twitter
|
||||
|
||||
### Authorization Flow (PIN-based)
|
||||
|
||||
An application can obtain a Twitter access `Token` for a user by requesting the user grant access via [3-legged](https://dev.twitter.com/oauth/3-legged) or [PIN-based](https://dev.twitter.com/oauth/pin-based) OAuth 1. Here is a command line example showing PIN-based authorization.
|
||||
|
||||
export TWITTER_CONSUMER_KEY=xxx
|
||||
export TWITTER_CONSUMER_SECRET=xxx
|
||||
go run twitter-login.go
|
||||
|
||||
The OAuth 1 flow can be used to implement Login with Twitter. Upon receiving an access token in a callback handler on your server, issue a user some form of unforgeable session identifier (i.e. cookie, token). Note that web backends should use a real `CallbackURL`, "oob" is for PIN-based agents such as the command line.
|
||||
|
||||
### Authorized Requests
|
||||
|
||||
Use the access `Token` to make requests on behalf of a Twitter user.
|
||||
|
||||
export TWITTER_CONSUMER_KEY=xxx
|
||||
export TWITTER_CONSUMER_SECRET=xxx
|
||||
export TWITTER_ACCESS_TOKEN=xxx
|
||||
export TWITTER_ACCESS_SECRET=xxx
|
||||
go run twitter-request.go
|
||||
|
||||
|
||||
## Tumblr
|
||||
|
||||
### Authorization Flow
|
||||
|
||||
An application can obtain a Tumblr access `Token` to act on behalf of a user. Here is a command line example which requests permission.
|
||||
|
||||
export TUMBLR_CONSUMER_KEY=xxx
|
||||
export TUMBLR_CONSUMER_SECRET=xxx
|
||||
go run tumblr-login.go
|
||||
|
||||
### Authorized Requests
|
||||
|
||||
Use the access `Token` to make requests on behalf of a Tumblr user.
|
||||
|
||||
export TUMBLR_CONSUMER_KEY=xxx
|
||||
export TUMBLR_CONSUMER_SECRET=xxx
|
||||
export TUMBLR_ACCESS_TOKEN=xxx
|
||||
export TUMBLR_ACCESS_SECRET=xxx
|
||||
go run tumblr-request.go
|
||||
|
||||
Note that only some Tumblr endpoints require OAuth1 signed requests, other endpoints require a special consumer key query parameter or no authorization.
|
||||
|
||||
68
vendor/github.com/dghubble/oauth1/examples/tumblr-login.go
generated
vendored
Normal file
68
vendor/github.com/dghubble/oauth1/examples/tumblr-login.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/dghubble/oauth1"
|
||||
"github.com/dghubble/oauth1/tumblr"
|
||||
)
|
||||
|
||||
var config oauth1.Config
|
||||
|
||||
// main performs the Tumblr OAuth1 user flow from the command line
|
||||
func main() {
|
||||
// read credentials from environment variables
|
||||
consumerKey := os.Getenv("TUMBLR_CONSUMER_KEY")
|
||||
consumerSecret := os.Getenv("TUMBLR_CONSUMER_SECRET")
|
||||
if consumerKey == "" || consumerSecret == "" {
|
||||
log.Fatal("Required environment variable missing.")
|
||||
}
|
||||
|
||||
config = oauth1.Config{
|
||||
ConsumerKey: consumerKey,
|
||||
ConsumerSecret: consumerSecret,
|
||||
// Tumblr does not support oob, uses consumer registered callback
|
||||
CallbackURL: "",
|
||||
Endpoint: tumblr.Endpoint,
|
||||
}
|
||||
|
||||
requestToken, requestSecret, err := login()
|
||||
if err != nil {
|
||||
log.Fatalf("Request Token Phase: %s", err.Error())
|
||||
}
|
||||
accessToken, err := receivePIN(requestToken, requestSecret)
|
||||
if err != nil {
|
||||
log.Fatalf("Access Token Phase: %s", err.Error())
|
||||
}
|
||||
|
||||
fmt.Println("Consumer was granted an access token to act on behalf of a user.")
|
||||
fmt.Printf("token: %s\nsecret: %s\n", accessToken.Token, accessToken.TokenSecret)
|
||||
}
|
||||
|
||||
func login() (requestToken, requestSecret string, err error) {
|
||||
requestToken, requestSecret, err = config.RequestToken()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
authorizationURL, err := config.AuthorizationURL(requestToken)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
fmt.Printf("Open this URL in your browser:\n%s\n", authorizationURL.String())
|
||||
return requestToken, requestSecret, err
|
||||
}
|
||||
|
||||
func receivePIN(requestToken, requestSecret string) (*oauth1.Token, error) {
|
||||
fmt.Printf("Choose whether to grant the application access.\nPaste " +
|
||||
"the oauth_verifier parameter (excluding trailing #_=_) from the " +
|
||||
"address bar: ")
|
||||
var verifier string
|
||||
_, err := fmt.Scanf("%s", &verifier)
|
||||
accessToken, accessSecret, err := config.AccessToken(requestToken, requestSecret, verifier)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return oauth1.NewToken(accessToken, accessSecret), err
|
||||
}
|
||||
37
vendor/github.com/dghubble/oauth1/examples/tumblr-request.go
generated
vendored
Normal file
37
vendor/github.com/dghubble/oauth1/examples/tumblr-request.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/dghubble/oauth1"
|
||||
)
|
||||
|
||||
// Tumblr access token (token credential) requests on behalf of a user
|
||||
func main() {
|
||||
// read credentials from environment variables
|
||||
consumerKey := os.Getenv("TUMBLR_CONSUMER_KEY")
|
||||
consumerSecret := os.Getenv("TUMBLR_CONSUMER_SECRET")
|
||||
accessToken := os.Getenv("TUMBLR_ACCESS_TOKEN")
|
||||
accessSecret := os.Getenv("TUMBLR_ACCESS_SECRET")
|
||||
if consumerKey == "" || consumerSecret == "" || accessToken == "" || accessSecret == "" {
|
||||
panic("Missing required environment variable")
|
||||
}
|
||||
|
||||
config := oauth1.NewConfig(consumerKey, consumerSecret)
|
||||
token := oauth1.NewToken(accessToken, accessSecret)
|
||||
|
||||
// httpClient will automatically authorize http.Request's
|
||||
httpClient := config.Client(oauth1.NoContext, token)
|
||||
|
||||
// get information about the current authenticated user
|
||||
path := "https://api.tumblr.com/v2/user/info"
|
||||
resp, _ := httpClient.Get(path)
|
||||
defer resp.Body.Close()
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Printf("Raw Response Body:\n%v\n", string(body))
|
||||
|
||||
// note: Tumblr requires OAuth signed requests for particular endpoints,
|
||||
// others just need a consumer key query parameter (its janky).
|
||||
}
|
||||
75
vendor/github.com/dghubble/oauth1/examples/twitter-login.go
generated
vendored
Normal file
75
vendor/github.com/dghubble/oauth1/examples/twitter-login.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/dghubble/oauth1"
|
||||
twauth "github.com/dghubble/oauth1/twitter"
|
||||
)
|
||||
|
||||
const outOfBand = "oob"
|
||||
|
||||
var config oauth1.Config
|
||||
|
||||
// main performs Twitter PIN-based 3-legged OAuth 1 from the command line
|
||||
func main() {
|
||||
// read credentials from environment variables
|
||||
consumerKey := os.Getenv("TWITTER_CONSUMER_KEY")
|
||||
consumerSecret := os.Getenv("TWITTER_CONSUMER_SECRET")
|
||||
if consumerKey == "" || consumerSecret == "" {
|
||||
log.Fatal("Required environment variable missing.")
|
||||
}
|
||||
|
||||
config = oauth1.Config{
|
||||
ConsumerKey: consumerKey,
|
||||
ConsumerSecret: consumerSecret,
|
||||
CallbackURL: outOfBand,
|
||||
Endpoint: twauth.AuthorizeEndpoint,
|
||||
}
|
||||
|
||||
requestToken, err := login()
|
||||
if err != nil {
|
||||
log.Fatalf("Request Token Phase: %s", err.Error())
|
||||
}
|
||||
accessToken, err := receivePIN(requestToken)
|
||||
if err != nil {
|
||||
log.Fatalf("Access Token Phase: %s", err.Error())
|
||||
}
|
||||
|
||||
fmt.Println("Consumer was granted an access token to act on behalf of a user.")
|
||||
fmt.Printf("token: %s\nsecret: %s\n", accessToken.Token, accessToken.TokenSecret)
|
||||
}
|
||||
|
||||
func login() (requestToken string, err error) {
|
||||
requestToken, _, err = config.RequestToken()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
authorizationURL, err := config.AuthorizationURL(requestToken)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
fmt.Printf("Open this URL in your browser:\n%s\n", authorizationURL.String())
|
||||
return requestToken, err
|
||||
}
|
||||
|
||||
func receivePIN(requestToken string) (*oauth1.Token, error) {
|
||||
fmt.Printf("Paste your PIN here: ")
|
||||
var verifier string
|
||||
_, err := fmt.Scanf("%s", &verifier)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Twitter ignores the oauth_signature on the access token request. The user
|
||||
// to which the request (temporary) token corresponds is already known on the
|
||||
// server. The request for a request token earlier was validated signed by
|
||||
// the consumer. Consumer applications can avoid keeping request token state
|
||||
// between authorization granting and callback handling.
|
||||
accessToken, accessSecret, err := config.AccessToken(requestToken, "secret does not matter", verifier)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return oauth1.NewToken(accessToken, accessSecret), err
|
||||
}
|
||||
39
vendor/github.com/dghubble/oauth1/examples/twitter-request.go
generated
vendored
Normal file
39
vendor/github.com/dghubble/oauth1/examples/twitter-request.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/dghubble/go-twitter/twitter"
|
||||
"github.com/dghubble/oauth1"
|
||||
)
|
||||
|
||||
// Twitter user-auth requests with an Access Token (token credential)
|
||||
func main() {
|
||||
// read credentials from environment variables
|
||||
consumerKey := os.Getenv("TWITTER_CONSUMER_KEY")
|
||||
consumerSecret := os.Getenv("TWITTER_CONSUMER_SECRET")
|
||||
accessToken := os.Getenv("TWITTER_ACCESS_TOKEN")
|
||||
accessSecret := os.Getenv("TWITTER_ACCESS_SECRET")
|
||||
if consumerKey == "" || consumerSecret == "" || accessToken == "" || accessSecret == "" {
|
||||
panic("Missing required environment variable")
|
||||
}
|
||||
|
||||
config := oauth1.NewConfig(consumerKey, consumerSecret)
|
||||
token := oauth1.NewToken(accessToken, accessSecret)
|
||||
|
||||
// httpClient will automatically authorize http.Request's
|
||||
httpClient := config.Client(oauth1.NoContext, token)
|
||||
|
||||
path := "https://api.twitter.com/1.1/statuses/home_timeline.json?count=2"
|
||||
resp, _ := httpClient.Get(path)
|
||||
defer resp.Body.Close()
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
fmt.Printf("Raw Response Body:\n%v\n", string(body))
|
||||
|
||||
// Nicer: Pass OAuth1 client to go-twitter API
|
||||
api := twitter.NewClient(httpClient)
|
||||
tweets, _, _ := api.Timelines.HomeTimeline(nil)
|
||||
fmt.Printf("User's HOME TIMELINE:\n%+v\n", tweets)
|
||||
}
|
||||
202
vendor/github.com/dghubble/oauth1/reference_test.go
generated
vendored
Normal file
202
vendor/github.com/dghubble/oauth1/reference_test.go
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
expectedVersion = "1.0"
|
||||
expectedSignatureMethod = "HMAC-SHA1"
|
||||
)
|
||||
|
||||
func TestTwitterRequestTokenAuthHeader(t *testing.T) {
|
||||
// example from https://dev.twitter.com/web/sign-in/implementing
|
||||
var unixTimestamp int64 = 1318467427
|
||||
expectedConsumerKey := "cChZNFj6T5R0TigYB9yd1w"
|
||||
expectedCallback := "http%3A%2F%2Flocalhost%2Fsign-in-with-twitter%2F"
|
||||
expectedSignature := "F1Li3tvehgcraF8DMJ7OyxO4w9Y%3D"
|
||||
expectedTimestamp := "1318467427"
|
||||
expectedNonce := "ea9ec8429b68d6b77cd5600adbbb0456"
|
||||
config := &Config{
|
||||
ConsumerKey: expectedConsumerKey,
|
||||
ConsumerSecret: "L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg",
|
||||
CallbackURL: "http://localhost/sign-in-with-twitter/",
|
||||
Endpoint: Endpoint{
|
||||
RequestTokenURL: "https://api.twitter.com/oauth/request_token",
|
||||
AuthorizeURL: "https://api.twitter.com/oauth/authorize",
|
||||
AccessTokenURL: "https://api.twitter.com/oauth/access_token",
|
||||
},
|
||||
}
|
||||
|
||||
auther := &auther{config, &fixedClock{time.Unix(unixTimestamp, 0)}, &fixedNoncer{expectedNonce}}
|
||||
req, err := http.NewRequest("POST", config.Endpoint.RequestTokenURL, nil)
|
||||
assert.Nil(t, err)
|
||||
err = auther.setRequestTokenAuthHeader(req)
|
||||
// assert the request for a request token is signed and has an oauth_callback
|
||||
assert.Nil(t, err)
|
||||
params := parseOAuthParamsOrFail(t, req.Header.Get(authorizationHeaderParam))
|
||||
assert.Equal(t, expectedCallback, params[oauthCallbackParam])
|
||||
assert.Equal(t, expectedSignature, params[oauthSignatureParam])
|
||||
// additional OAuth parameters
|
||||
assert.Equal(t, expectedConsumerKey, params[oauthConsumerKeyParam])
|
||||
assert.Equal(t, expectedNonce, params[oauthNonceParam])
|
||||
assert.Equal(t, expectedTimestamp, params[oauthTimestampParam])
|
||||
assert.Equal(t, expectedVersion, params[oauthVersionParam])
|
||||
assert.Equal(t, expectedSignatureMethod, params[oauthSignatureMethodParam])
|
||||
}
|
||||
|
||||
func TestTwitterAccessTokenAuthHeader(t *testing.T) {
|
||||
// example from https://dev.twitter.com/web/sign-in/implementing
|
||||
var unixTimestamp int64 = 1318467427
|
||||
expectedConsumerKey := "cChZNFj6T5R0TigYB9yd1w"
|
||||
expectedRequestToken := "NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0"
|
||||
requestTokenSecret := "veNRnAWe6inFuo8o2u8SLLZLjolYDmDP7SzL0YfYI"
|
||||
expectedVerifier := "uw7NjWHT6OJ1MpJOXsHfNxoAhPKpgI8BlYDhxEjIBY"
|
||||
expectedSignature := "39cipBtIOHEEnybAR4sATQTpl2I%3D"
|
||||
expectedTimestamp := "1318467427"
|
||||
expectedNonce := "a9900fe68e2573b27a37f10fbad6a755"
|
||||
config := &Config{
|
||||
ConsumerKey: expectedConsumerKey,
|
||||
ConsumerSecret: "L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg",
|
||||
Endpoint: Endpoint{
|
||||
RequestTokenURL: "https://api.twitter.com/oauth/request_token",
|
||||
AuthorizeURL: "https://api.twitter.com/oauth/authorize",
|
||||
AccessTokenURL: "https://api.twitter.com/oauth/access_token",
|
||||
},
|
||||
}
|
||||
|
||||
auther := &auther{config, &fixedClock{time.Unix(unixTimestamp, 0)}, &fixedNoncer{expectedNonce}}
|
||||
req, err := http.NewRequest("POST", config.Endpoint.AccessTokenURL, nil)
|
||||
assert.Nil(t, err)
|
||||
err = auther.setAccessTokenAuthHeader(req, expectedRequestToken, requestTokenSecret, expectedVerifier)
|
||||
// assert the request for an access token is signed and has an oauth_token and verifier
|
||||
assert.Nil(t, err)
|
||||
params := parseOAuthParamsOrFail(t, req.Header.Get(authorizationHeaderParam))
|
||||
assert.Equal(t, expectedRequestToken, params[oauthTokenParam])
|
||||
assert.Equal(t, expectedVerifier, params[oauthVerifierParam])
|
||||
assert.Equal(t, expectedSignature, params[oauthSignatureParam])
|
||||
// additional OAuth parameters
|
||||
assert.Equal(t, expectedConsumerKey, params[oauthConsumerKeyParam])
|
||||
assert.Equal(t, expectedNonce, params[oauthNonceParam])
|
||||
assert.Equal(t, expectedTimestamp, params[oauthTimestampParam])
|
||||
assert.Equal(t, expectedVersion, params[oauthVersionParam])
|
||||
assert.Equal(t, expectedSignatureMethod, params[oauthSignatureMethodParam])
|
||||
}
|
||||
|
||||
// example from https://dev.twitter.com/oauth/overview/authorizing-requests,
|
||||
// https://dev.twitter.com/oauth/overview/creating-signatures, and
|
||||
// https://dev.twitter.com/oauth/application-only
|
||||
var unixTimestampOfRequest int64 = 1318622958
|
||||
var expectedTwitterConsumerKey = "xvz1evFS4wEEPTGEFPHBog"
|
||||
var expectedTwitterOAuthToken = "370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb"
|
||||
var expectedNonce = "kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg"
|
||||
var twitterConfig = &Config{
|
||||
ConsumerKey: expectedTwitterConsumerKey,
|
||||
ConsumerSecret: "kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw",
|
||||
Endpoint: Endpoint{
|
||||
RequestTokenURL: "https://api.twitter.com/oauth/request_token",
|
||||
AuthorizeURL: "https://api.twitter.com/oauth/authorize",
|
||||
AccessTokenURL: "https://api.twitter.com/oauth/access_token",
|
||||
},
|
||||
}
|
||||
|
||||
func TestTwitterParameterString(t *testing.T) {
|
||||
auther := &auther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}, &fixedNoncer{expectedNonce}}
|
||||
values := url.Values{}
|
||||
values.Add("status", "Hello Ladies + Gentlemen, a signed OAuth request!")
|
||||
// note: the reference example is old and uses api v1 in the URL
|
||||
req, err := http.NewRequest("post", "https://api.twitter.com/1/statuses/update.json?include_entities=true", strings.NewReader(values.Encode()))
|
||||
assert.Nil(t, err)
|
||||
req.Header.Set(contentType, formContentType)
|
||||
oauthParams := auther.commonOAuthParams()
|
||||
oauthParams[oauthTokenParam] = expectedTwitterOAuthToken
|
||||
params, err := collectParameters(req, oauthParams)
|
||||
// assert that the parameter string matches the reference
|
||||
expectedParameterString := "include_entities=true&oauth_consumer_key=xvz1evFS4wEEPTGEFPHBog&oauth_nonce=kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1318622958&oauth_token=370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb&oauth_version=1.0&status=Hello%20Ladies%20%2B%20Gentlemen%2C%20a%20signed%20OAuth%20request%21"
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expectedParameterString, normalizedParameterString(params))
|
||||
}
|
||||
|
||||
func TestTwitterSignatureBase(t *testing.T) {
|
||||
auther := &auther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}, &fixedNoncer{expectedNonce}}
|
||||
values := url.Values{}
|
||||
values.Add("status", "Hello Ladies + Gentlemen, a signed OAuth request!")
|
||||
// note: the reference example is old and uses api v1 in the URL
|
||||
req, err := http.NewRequest("post", "https://api.twitter.com/1/statuses/update.json?include_entities=true", strings.NewReader(values.Encode()))
|
||||
assert.Nil(t, err)
|
||||
req.Header.Set(contentType, formContentType)
|
||||
oauthParams := auther.commonOAuthParams()
|
||||
oauthParams[oauthTokenParam] = expectedTwitterOAuthToken
|
||||
params, err := collectParameters(req, oauthParams)
|
||||
signatureBase := signatureBase(req, params)
|
||||
// assert that the signature base string matches the reference
|
||||
// checks that method is uppercased, url is encoded, parameter string is added, all joined by &
|
||||
expectedSignatureBase := "POST&https%3A%2F%2Fapi.twitter.com%2F1%2Fstatuses%2Fupdate.json&include_entities%3Dtrue%26oauth_consumer_key%3Dxvz1evFS4wEEPTGEFPHBog%26oauth_nonce%3DkYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1318622958%26oauth_token%3D370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb%26oauth_version%3D1.0%26status%3DHello%2520Ladies%2520%252B%2520Gentlemen%252C%2520a%2520signed%2520OAuth%2520request%2521"
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, expectedSignatureBase, signatureBase)
|
||||
}
|
||||
|
||||
func TestTwitterRequestAuthHeader(t *testing.T) {
|
||||
oauthTokenSecret := "LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE"
|
||||
expectedSignature := PercentEncode("tnnArxj06cWHq44gCs1OSKk/jLY=")
|
||||
expectedTimestamp := "1318622958"
|
||||
|
||||
auther := &auther{twitterConfig, &fixedClock{time.Unix(unixTimestampOfRequest, 0)}, &fixedNoncer{expectedNonce}}
|
||||
values := url.Values{}
|
||||
values.Add("status", "Hello Ladies + Gentlemen, a signed OAuth request!")
|
||||
|
||||
accessToken := &Token{expectedTwitterOAuthToken, oauthTokenSecret}
|
||||
req, err := http.NewRequest("POST", "https://api.twitter.com/1/statuses/update.json?include_entities=true", strings.NewReader(values.Encode()))
|
||||
assert.Nil(t, err)
|
||||
req.Header.Set(contentType, formContentType)
|
||||
err = auther.setRequestAuthHeader(req, accessToken)
|
||||
// assert that request is signed and has an access token token
|
||||
assert.Nil(t, err)
|
||||
params := parseOAuthParamsOrFail(t, req.Header.Get(authorizationHeaderParam))
|
||||
assert.Equal(t, expectedTwitterOAuthToken, params[oauthTokenParam])
|
||||
assert.Equal(t, expectedSignature, params[oauthSignatureParam])
|
||||
// additional OAuth parameters
|
||||
assert.Equal(t, expectedTwitterConsumerKey, params[oauthConsumerKeyParam])
|
||||
assert.Equal(t, expectedNonce, params[oauthNonceParam])
|
||||
assert.Equal(t, expectedSignatureMethod, params[oauthSignatureMethodParam])
|
||||
assert.Equal(t, expectedTimestamp, params[oauthTimestampParam])
|
||||
assert.Equal(t, expectedVersion, params[oauthVersionParam])
|
||||
}
|
||||
|
||||
func parseOAuthParamsOrFail(t *testing.T, authHeader string) map[string]string {
|
||||
if !strings.HasPrefix(authHeader, authorizationPrefix) {
|
||||
assert.Fail(t, fmt.Sprintf("Expected Authorization header to start with \"%s\", got \"%s\"", authorizationPrefix, authHeader[:len(authorizationPrefix)+1]))
|
||||
}
|
||||
params := map[string]string{}
|
||||
for _, pairStr := range strings.Split(authHeader[len(authorizationPrefix):], ", ") {
|
||||
pair := strings.Split(pairStr, "=")
|
||||
if len(pair) != 2 {
|
||||
assert.Fail(t, "Error parsing OAuth parameter %s", pairStr)
|
||||
}
|
||||
params[pair[0]] = strings.Replace(pair[1], "\"", "", -1)
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
type fixedClock struct {
|
||||
now time.Time
|
||||
}
|
||||
|
||||
func (c *fixedClock) Now() time.Time {
|
||||
return c.now
|
||||
}
|
||||
|
||||
type fixedNoncer struct {
|
||||
nonce string
|
||||
}
|
||||
|
||||
func (n *fixedNoncer) Nonce() string {
|
||||
return n.nonce
|
||||
}
|
||||
62
vendor/github.com/dghubble/oauth1/signer.go
generated
vendored
Normal file
62
vendor/github.com/dghubble/oauth1/signer.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A Signer signs messages to create signed OAuth1 Requests.
|
||||
type Signer interface {
|
||||
// Name returns the name of the signing method.
|
||||
Name() string
|
||||
// Sign signs the message using the given secret key.
|
||||
Sign(key string, message string) (string, error)
|
||||
}
|
||||
|
||||
// HMACSigner signs messages with an HMAC SHA1 digest, using the concatenated
|
||||
// consumer secret and token secret as the key.
|
||||
type HMACSigner struct {
|
||||
ConsumerSecret string
|
||||
}
|
||||
|
||||
// Name returns the HMAC-SHA1 method.
|
||||
func (s *HMACSigner) Name() string {
|
||||
return "HMAC-SHA1"
|
||||
}
|
||||
|
||||
// Sign creates a concatenated consumer and token secret key and calculates
|
||||
// the HMAC digest of the message. Returns the base64 encoded digest bytes.
|
||||
func (s *HMACSigner) Sign(tokenSecret, message string) (string, error) {
|
||||
signingKey := strings.Join([]string{s.ConsumerSecret, tokenSecret}, "&")
|
||||
mac := hmac.New(sha1.New, []byte(signingKey))
|
||||
mac.Write([]byte(message))
|
||||
signatureBytes := mac.Sum(nil)
|
||||
return base64.StdEncoding.EncodeToString(signatureBytes), nil
|
||||
}
|
||||
|
||||
// RSASigner RSA PKCS1-v1_5 signs SHA1 digests of messages using the given
|
||||
// RSA private key.
|
||||
type RSASigner struct {
|
||||
PrivateKey *rsa.PrivateKey
|
||||
}
|
||||
|
||||
// Name returns the RSA-SHA1 method.
|
||||
func (s *RSASigner) Name() string {
|
||||
return "RSA-SHA1"
|
||||
}
|
||||
|
||||
// Sign uses RSA PKCS1-v1_5 to sign a SHA1 digest of the given message. The
|
||||
// tokenSecret is not used with this signing scheme.
|
||||
func (s *RSASigner) Sign(tokenSecret, message string) (string, error) {
|
||||
digest := sha1.Sum([]byte(message))
|
||||
signature, err := rsa.SignPKCS1v15(rand.Reader, s.PrivateKey, crypto.SHA1, digest[:])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(signature), nil
|
||||
}
|
||||
23
vendor/github.com/dghubble/oauth1/test
generated
vendored
Executable file
23
vendor/github.com/dghubble/oauth1/test
generated
vendored
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
PKGS=$(go list ./... | grep -v /examples)
|
||||
FORMATTABLE="$(find . -maxdepth 1 -type d)"
|
||||
LINTABLE=$(go list ./...)
|
||||
|
||||
go test $PKGS -cover
|
||||
go vet $PKGS
|
||||
|
||||
echo "Checking gofmt..."
|
||||
fmtRes=$(gofmt -l $FORMATTABLE)
|
||||
if [ -n "${fmtRes}" ]; then
|
||||
echo -e "gofmt checking failed:\n${fmtRes}"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "Checking golint..."
|
||||
lintRes=$(echo $LINTABLE | xargs -n 1 golint)
|
||||
if [ -n "${lintRes}" ]; then
|
||||
echo -e "golint checking failed:\n${lintRes}"
|
||||
exit 2
|
||||
fi
|
||||
43
vendor/github.com/dghubble/oauth1/token.go
generated
vendored
Normal file
43
vendor/github.com/dghubble/oauth1/token.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// A TokenSource can return a Token.
|
||||
type TokenSource interface {
|
||||
Token() (*Token, error)
|
||||
}
|
||||
|
||||
// Token is an AccessToken (token credential) which allows a consumer (client)
|
||||
// to access resources from an OAuth1 provider server.
|
||||
type Token struct {
|
||||
Token string
|
||||
TokenSecret string
|
||||
}
|
||||
|
||||
// NewToken returns a new Token with the given token and token secret.
|
||||
func NewToken(token, tokenSecret string) *Token {
|
||||
return &Token{
|
||||
Token: token,
|
||||
TokenSecret: tokenSecret,
|
||||
}
|
||||
}
|
||||
|
||||
// StaticTokenSource returns a TokenSource which always returns the same Token.
|
||||
// This is appropriate for tokens which do not have a time expiration.
|
||||
func StaticTokenSource(token *Token) TokenSource {
|
||||
return staticTokenSource{token}
|
||||
}
|
||||
|
||||
// staticTokenSource is a TokenSource that always returns the same Token.
|
||||
type staticTokenSource struct {
|
||||
token *Token
|
||||
}
|
||||
|
||||
func (s staticTokenSource) Token() (*Token, error) {
|
||||
if s.token == nil {
|
||||
return nil, errors.New("oauth1: Token is nil")
|
||||
}
|
||||
return s.token, nil
|
||||
}
|
||||
31
vendor/github.com/dghubble/oauth1/token_test.go
generated
vendored
Normal file
31
vendor/github.com/dghubble/oauth1/token_test.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewToken(t *testing.T) {
|
||||
expectedToken := "token"
|
||||
expectedSecret := "secret"
|
||||
tk := NewToken(expectedToken, expectedSecret)
|
||||
assert.Equal(t, expectedToken, tk.Token)
|
||||
assert.Equal(t, expectedSecret, tk.TokenSecret)
|
||||
}
|
||||
|
||||
func TestStaticTokenSource(t *testing.T) {
|
||||
ts := StaticTokenSource(NewToken("t", "s"))
|
||||
tk, err := ts.Token()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "t", tk.Token)
|
||||
}
|
||||
|
||||
func TestStaticTokenSourceEmpty(t *testing.T) {
|
||||
ts := StaticTokenSource(nil)
|
||||
tk, err := ts.Token()
|
||||
assert.Nil(t, tk)
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, "oauth1: Token is nil", err.Error())
|
||||
}
|
||||
}
|
||||
65
vendor/github.com/dghubble/oauth1/transport.go
generated
vendored
Normal file
65
vendor/github.com/dghubble/oauth1/transport.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Transport is an http.RoundTripper which makes OAuth1 HTTP requests. It
|
||||
// wraps a base RoundTripper and adds an Authorization header using the
|
||||
// token from a TokenSource.
|
||||
//
|
||||
// Transport is a low-level component, most users should use Config to create
|
||||
// an http.Client instead.
|
||||
type Transport struct {
|
||||
// Base is the base RoundTripper used to make HTTP requests. If nil, then
|
||||
// http.DefaultTransport is used
|
||||
Base http.RoundTripper
|
||||
// source supplies the token to use when signing a request
|
||||
source TokenSource
|
||||
// auther adds OAuth1 Authorization headers to requests
|
||||
auther *auther
|
||||
}
|
||||
|
||||
// RoundTrip authorizes the request with a signed OAuth1 Authorization header
|
||||
// using the auther and TokenSource.
|
||||
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if t.source == nil {
|
||||
return nil, fmt.Errorf("oauth1: Transport's source is nil")
|
||||
}
|
||||
accessToken, err := t.source.Token()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t.auther == nil {
|
||||
return nil, fmt.Errorf("oauth1: Transport's auther is nil")
|
||||
}
|
||||
// RoundTripper should not modify the given request, clone it
|
||||
req2 := cloneRequest(req)
|
||||
err = t.auther.setRequestAuthHeader(req2, accessToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t.base().RoundTrip(req2)
|
||||
}
|
||||
|
||||
func (t *Transport) base() http.RoundTripper {
|
||||
if t.Base != nil {
|
||||
return t.Base
|
||||
}
|
||||
return http.DefaultTransport
|
||||
}
|
||||
|
||||
// cloneRequest returns a clone of the given *http.Request with a shallow
|
||||
// copy of struct fields and a deep copy of the Header map.
|
||||
func cloneRequest(req *http.Request) *http.Request {
|
||||
// shallow copy the struct
|
||||
r2 := new(http.Request)
|
||||
*r2 = *req
|
||||
// deep copy Header so setting a header on the clone does not affect original
|
||||
r2.Header = make(http.Header, len(req.Header))
|
||||
for k, s := range req.Header {
|
||||
r2.Header[k] = append([]string(nil), s...)
|
||||
}
|
||||
return r2
|
||||
}
|
||||
117
vendor/github.com/dghubble/oauth1/transport_test.go
generated
vendored
Normal file
117
vendor/github.com/dghubble/oauth1/transport_test.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
package oauth1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTransport(t *testing.T) {
|
||||
const (
|
||||
expectedToken = "access_token"
|
||||
expectedConsumerKey = "consumer_key"
|
||||
expectedNonce = "some_nonce"
|
||||
expectedSignatureMethod = "HMAC-SHA1"
|
||||
expectedTimestamp = "123456789"
|
||||
)
|
||||
server := newMockServer(func(w http.ResponseWriter, req *http.Request) {
|
||||
params := parseOAuthParamsOrFail(t, req.Header.Get("Authorization"))
|
||||
assert.Equal(t, expectedToken, params[oauthTokenParam])
|
||||
assert.Equal(t, expectedConsumerKey, params[oauthConsumerKeyParam])
|
||||
assert.Equal(t, expectedNonce, params[oauthNonceParam])
|
||||
assert.Equal(t, expectedSignatureMethod, params[oauthSignatureMethodParam])
|
||||
assert.Equal(t, expectedTimestamp, params[oauthTimestampParam])
|
||||
assert.Equal(t, defaultOauthVersion, params[oauthVersionParam])
|
||||
// oauth_signature will vary, httptest.Server uses a random port
|
||||
})
|
||||
defer server.Close()
|
||||
|
||||
config := &Config{
|
||||
ConsumerKey: expectedConsumerKey,
|
||||
ConsumerSecret: "consumer_secret",
|
||||
}
|
||||
auther := &auther{
|
||||
config: config,
|
||||
clock: &fixedClock{time.Unix(123456789, 0)},
|
||||
noncer: &fixedNoncer{expectedNonce},
|
||||
}
|
||||
tr := &Transport{
|
||||
source: StaticTokenSource(NewToken(expectedToken, "some_secret")),
|
||||
auther: auther,
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
|
||||
req, err := http.NewRequest("GET", server.URL, nil)
|
||||
assert.Nil(t, err)
|
||||
_, err = client.Do(req)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestTransport_defaultBaseTransport(t *testing.T) {
|
||||
tr := &Transport{
|
||||
Base: nil,
|
||||
}
|
||||
assert.Equal(t, http.DefaultTransport, tr.base())
|
||||
}
|
||||
|
||||
func TestTransport_customBaseTransport(t *testing.T) {
|
||||
expected := &http.Transport{}
|
||||
tr := &Transport{
|
||||
Base: expected,
|
||||
}
|
||||
assert.Equal(t, expected, tr.base())
|
||||
}
|
||||
|
||||
func TestTransport_nilSource(t *testing.T) {
|
||||
tr := &Transport{
|
||||
source: nil,
|
||||
auther: &auther{
|
||||
config: &Config{},
|
||||
clock: &fixedClock{time.Unix(123456789, 0)},
|
||||
noncer: &fixedNoncer{"any_nonce"},
|
||||
},
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
resp, err := client.Get("http://example.com")
|
||||
assert.Nil(t, resp)
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, "Get http://example.com: oauth1: Transport's source is nil", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransport_emptySource(t *testing.T) {
|
||||
tr := &Transport{
|
||||
source: StaticTokenSource(nil),
|
||||
auther: &auther{
|
||||
config: &Config{},
|
||||
clock: &fixedClock{time.Unix(123456789, 0)},
|
||||
noncer: &fixedNoncer{"any_nonce"},
|
||||
},
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
resp, err := client.Get("http://example.com")
|
||||
assert.Nil(t, resp)
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, "Get http://example.com: oauth1: Token is nil", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransport_nilAuther(t *testing.T) {
|
||||
tr := &Transport{
|
||||
source: StaticTokenSource(&Token{}),
|
||||
auther: nil,
|
||||
}
|
||||
client := &http.Client{Transport: tr}
|
||||
resp, err := client.Get("http://example.com")
|
||||
assert.Nil(t, resp)
|
||||
if assert.Error(t, err) {
|
||||
assert.Equal(t, "Get http://example.com: oauth1: Transport's auther is nil", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func newMockServer(handler func(w http.ResponseWriter, r *http.Request)) *httptest.Server {
|
||||
return httptest.NewServer(http.HandlerFunc(handler))
|
||||
}
|
||||
13
vendor/github.com/dghubble/oauth1/tumblr/tumblr.go
generated
vendored
Normal file
13
vendor/github.com/dghubble/oauth1/tumblr/tumblr.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Package tumblr provides constants for using OAuth 1 to access Tumblr.
|
||||
package tumblr
|
||||
|
||||
import (
|
||||
"github.com/dghubble/oauth1"
|
||||
)
|
||||
|
||||
// Endpoint is Tumblr's OAuth 1a endpoint.
|
||||
var Endpoint = oauth1.Endpoint{
|
||||
RequestTokenURL: "http://www.tumblr.com/oauth/request_token",
|
||||
AuthorizeURL: "http://www.tumblr.com/oauth/authorize",
|
||||
AccessTokenURL: "http://www.tumblr.com/oauth/access_token",
|
||||
}
|
||||
25
vendor/github.com/dghubble/oauth1/twitter/twitter.go
generated
vendored
Normal file
25
vendor/github.com/dghubble/oauth1/twitter/twitter.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Package twitter provides constants for using OAuth1 to access Twitter.
|
||||
package twitter
|
||||
|
||||
import (
|
||||
"github.com/dghubble/oauth1"
|
||||
)
|
||||
|
||||
// AuthenticateEndpoint is Twitter's OAuth 1 endpoint which uses the
|
||||
// oauth/authenticate AuthorizeURL redirect. Logged in users who have granted
|
||||
// access are immediately authenticated and redirected to the callback URL.
|
||||
var AuthenticateEndpoint = oauth1.Endpoint{
|
||||
RequestTokenURL: "https://api.twitter.com/oauth/request_token",
|
||||
AuthorizeURL: "https://api.twitter.com/oauth/authenticate",
|
||||
AccessTokenURL: "https://api.twitter.com/oauth/access_token",
|
||||
}
|
||||
|
||||
// AuthorizeEndpoint is Twitter's OAuth 1 endpoint which uses the
|
||||
// oauth/authorize AuthorizeURL redirect. Note that this requires users who
|
||||
// have granted access previously, to re-grant access at AuthorizeURL.
|
||||
// Prefer AuthenticateEndpoint over AuthorizeEndpoint if you are unsure.
|
||||
var AuthorizeEndpoint = oauth1.Endpoint{
|
||||
RequestTokenURL: "https://api.twitter.com/oauth/request_token",
|
||||
AuthorizeURL: "https://api.twitter.com/oauth/authorize",
|
||||
AccessTokenURL: "https://api.twitter.com/oauth/access_token",
|
||||
}
|
||||
13
vendor/github.com/dghubble/oauth1/xing/xing.go
generated
vendored
Normal file
13
vendor/github.com/dghubble/oauth1/xing/xing.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Package xing provides constants for using OAuth1 to access Xing.
|
||||
package xing
|
||||
|
||||
import (
|
||||
"github.com/dghubble/oauth1"
|
||||
)
|
||||
|
||||
// Endpoint is Xing's OAuth 1 endpoint.
|
||||
var Endpoint = oauth1.Endpoint{
|
||||
RequestTokenURL: "https://api.xing.com/v1/request_token",
|
||||
AuthorizeURL: "https://api.xing.com/v1/authorize",
|
||||
AccessTokenURL: "https://api.xing.com/v1/access_token",
|
||||
}
|
||||
Reference in New Issue
Block a user