diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 923453d..1dd09f9 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,22 +1,21 @@ { "ImportPath": "github.com/concourse/github-release-resource", - "GoVersion": "go1.5.1", + "GoVersion": "go1.5", "Packages": [ "./..." ], "Deps": [ - { - "ImportPath": "code.google.com/p/goauth2/oauth", - "Comment": "weekly-57", - "Rev": "'222e66a2882ef45c3153c86c0aa82fe65fc29a78'" - }, { "ImportPath": "github.com/cppforlife/go-semi-semantic/version", "Rev": "a7ba07c61e58e6df7693d49a13d24f533182518a" }, { "ImportPath": "github.com/golang/protobuf/proto", - "Rev": "d3d78384b82d449651d2435ed329d70f7c48aa56" + "Rev": "45bba206dd5270d96bac4942dcfe515726613249" + }, + { + "ImportPath": "github.com/google/go-github/github", + "Rev": "b8b4ac742977310ff6e75140a403a38dab109977" }, { "ImportPath": "github.com/google/go-querystring/query", @@ -28,17 +27,21 @@ }, { "ImportPath": "github.com/onsi/ginkgo", - "Comment": "v1.2.0-22-g39d2c24", - "Rev": "39d2c24f8a92c88f7e7f4d8ec6c80d3cc8f5ac65" + "Comment": "v1.2.0-36-g3341026", + "Rev": "33410268c3881642c746ada23c04eeb331212596" }, { "ImportPath": "github.com/onsi/gomega", - "Comment": "v1.0-71-g2152b45", - "Rev": "2152b45fa28a361beba9aab0885972323a444e28" + "Comment": "v1.0-77-g28f13bc", + "Rev": "28f13bc54cf0f72e3fa395ba9d3added0b3b849c" }, { - "ImportPath": "github.com/zachgersh/go-github/github", - "Rev": "f47a8b33261f10482dfede3cc839c4fcf34da04f" + "ImportPath": "golang.org/x/net/context", + "Rev": "04b9de9b512f58addf28c9853d50ebef61c3953e" + }, + { + "ImportPath": "golang.org/x/oauth2", + "Rev": "8a57ed94ffd43444c0879fe75701732a38afc985" } ] } diff --git a/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/example/oauthreq.go b/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/example/oauthreq.go deleted file mode 100644 index f9651bd..0000000 --- a/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/example/oauthreq.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2011 The goauth2 Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This program makes a call to the specified API, authenticated with OAuth2. -// a list of example APIs can be found at https://code.google.com/oauthplayground/ -package main - -import ( - "flag" - "fmt" - "io" - "log" - "os" - - "code.google.com/p/goauth2/oauth" -) - -var ( - clientId = flag.String("id", "", "Client ID") - clientSecret = flag.String("secret", "", "Client Secret") - scope = flag.String("scope", "https://www.googleapis.com/auth/userinfo.profile", "OAuth scope") - redirectURL = flag.String("redirect_url", "oob", "Redirect URL") - authURL = flag.String("auth_url", "https://accounts.google.com/o/oauth2/auth", "Authentication URL") - tokenURL = flag.String("token_url", "https://accounts.google.com/o/oauth2/token", "Token URL") - requestURL = flag.String("request_url", "https://www.googleapis.com/oauth2/v1/userinfo", "API request") - code = flag.String("code", "", "Authorization Code") - cachefile = flag.String("cache", "cache.json", "Token cache file") -) - -const usageMsg = ` -To obtain a request token you must specify both -id and -secret. - -To obtain Client ID and Secret, see the "OAuth 2 Credentials" section under -the "API Access" tab on this page: https://code.google.com/apis/console/ - -Once you have completed the OAuth flow, the credentials should be stored inside -the file specified by -cache and you may run without the -id and -secret flags. -` - -func main() { - flag.Parse() - - // Set up a configuration. - config := &oauth.Config{ - ClientId: *clientId, - ClientSecret: *clientSecret, - RedirectURL: *redirectURL, - Scope: *scope, - AuthURL: *authURL, - TokenURL: *tokenURL, - TokenCache: oauth.CacheFile(*cachefile), - } - - // Set up a Transport using the config. - transport := &oauth.Transport{Config: config} - - // Try to pull the token from the cache; if this fails, we need to get one. - token, err := config.TokenCache.Token() - if err != nil { - if *clientId == "" || *clientSecret == "" { - flag.Usage() - fmt.Fprint(os.Stderr, usageMsg) - os.Exit(2) - } - if *code == "" { - // Get an authorization code from the data provider. - // ("Please ask the user if I can access this resource.") - url := config.AuthCodeURL("") - fmt.Print("Visit this URL to get a code, then run again with -code=YOUR_CODE\n\n") - fmt.Println(url) - return - } - // Exchange the authorization code for an access token. - // ("Here's the code you gave the user, now give me a token!") - token, err = transport.Exchange(*code) - if err != nil { - log.Fatal("Exchange:", err) - } - // (The Exchange method will automatically cache the token.) - fmt.Printf("Token is cached in %v\n", config.TokenCache) - } - - // Make the actual request using the cached token to authenticate. - // ("Here's the token, let me in!") - transport.Token = token - - // Make the request. - r, err := transport.Client().Get(*requestURL) - if err != nil { - log.Fatal("Get:", err) - } - defer r.Body.Close() - - // Write the response to standard output. - io.Copy(os.Stdout, r.Body) - - // Send final carriage return, just to be neat. - fmt.Println() -} diff --git a/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/jwt/example/example.client_secrets.json b/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/jwt/example/example.client_secrets.json deleted file mode 100644 index 2ea86f2..0000000 --- a/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/jwt/example/example.client_secrets.json +++ /dev/null @@ -1 +0,0 @@ -{"web":{"auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://accounts.google.com/o/oauth2/token","client_email":"XXXXXXXXXXXX@developer.gserviceaccount.com","client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/XXXXXXXXXXXX@developer.gserviceaccount.com","client_id":"XXXXXXXXXXXX.apps.googleusercontent.com","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs"}} diff --git a/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/jwt/example/example.pem b/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/jwt/example/example.pem deleted file mode 100644 index 8f78b92..0000000 --- a/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/jwt/example/example.pem +++ /dev/null @@ -1,20 +0,0 @@ -Bag Attributes - friendlyName: privatekey - localKeyID: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -Key Attributes: ------BEGIN PRIVATE KEY----- -XXXXxyXXXXXXXxxyxxxX9y0XXYXXXXYXXxXyxxXxXxXXXyXXXXx4yx1xy1xyYxxY -1XxYy38YxXxxxyXxyyxx+xxxxyx1Y1xYx7yx2/Y1XyyXYYYxY5YXxX0xY/Y642yX -zYYxYXzXYxY0Y8y9YxyYXxxX40YyXxxXX4XXxx7XxXxxXyXxYYXxXyxX5XY0Yy2X -1YX0XXyy6YXyXx9XxXxyXX9XXYXxXxXXXXXXxYXYY3Y8Yy311XYYY81XyY14Xyyx -xXyx7xxXXXxxxxyyyX4YYYXyYyYXyxX4XYXYyxXYyx9xy23xXYyXyxYxXxx1XXXY -y98yX6yYxyyyX4Xyx1Xy/0yxxYxXxYYx2xx7yYXXXxYXXXxyXyyYYxx5XX2xxyxy -y6Yyyx0XX3YYYyx9YYXXXX7y0yxXXy+90XYz1y2xyx7yXxX+8X0xYxXXYxxyxYYy -YXx8Yy4yX0Xyxxx6yYX92yxy1YYYzyyyyxy55x/yyXXXYYXYXXzXXxYYxyXY8XXX -+y9+yXxX7XxxyYYxxXYxyY623xxXxYX59x5Y6yYyXYY4YxXXYXXXYxXYxXxXXx6x -YXX7XxXX2X0XY7YXyYy1XXxYXxXxYY1xXXxxxyy+07zXYxYxxXyyxxyxXx1XYy5X -5XYzyxYxXXYyX9XX7xX8xXxx+XXYyYXXXX5YY1x8Yxyx54Xy/1XXyyYXY5YxYyxY -XyyxXyX/YxxXXXxXXYXxyxx63xX/xxyYXXyYzx0XY+YxX5xyYyyxxxXXYX/94XXy -Xx63xYxXyXY3/XXxyyXX15XXXyz08XYY5YYXY/YXy/96x68XyyXXxYyXy4xYXx5x -7yxxyxxYxXxyx3y= ------END PRIVATE KEY----- diff --git a/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/jwt/example/main.go b/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/jwt/example/main.go deleted file mode 100644 index 2256e9c..0000000 --- a/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/jwt/example/main.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2011 The goauth2 Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This program makes a read only call to the Google Cloud Storage API, -// authenticated with OAuth2. A list of example APIs can be found at -// https://code.google.com/oauthplayground/ -package main - -import ( - "encoding/json" - "flag" - "fmt" - "io/ioutil" - "log" - "net/http" - "strings" - - "code.google.com/p/goauth2/oauth/jwt" -) - -const scope = "https://www.googleapis.com/auth/devstorage.read_only" - -var ( - secretsFile = flag.String("s", "", "JSON encoded secrets for the service account") - pemFile = flag.String("k", "", "private pem key file for the service account") -) - -const usageMsg = ` -You must specify -k and -s. - -To obtain client secrets and pem, see the "OAuth 2 Credentials" section under -the "API Access" tab on this page: https://code.google.com/apis/console/ - -Google Cloud Storage must also be turned on in the API console. -` - -func main() { - flag.Parse() - - if *secretsFile == "" || *pemFile == "" { - flag.Usage() - fmt.Println(usageMsg) - return - } - - // Read the secret file bytes into the config. - secretBytes, err := ioutil.ReadFile(*secretsFile) - if err != nil { - log.Fatal("error reading secerets file:", err) - } - var config struct { - Web struct { - ClientEmail string `json:"client_email"` - ClientID string `json:"client_id"` - TokenURI string `json:"token_uri"` - } - } - err = json.Unmarshal(secretBytes, &config) - if err != nil { - log.Fatal("error unmarshalling secerets:", err) - } - - // Get the project ID from the client ID. - projectID := strings.SplitN(config.Web.ClientID, "-", 2)[0] - - // Read the pem file bytes for the private key. - keyBytes, err := ioutil.ReadFile(*pemFile) - if err != nil { - log.Fatal("error reading private key file:", err) - } - - // Craft the ClaimSet and JWT token. - t := jwt.NewToken(config.Web.ClientEmail, scope, keyBytes) - t.ClaimSet.Aud = config.Web.TokenURI - - // We need to provide a client. - c := &http.Client{} - - // Get the access token. - o, err := t.Assert(c) - if err != nil { - log.Fatal("assertion error:", err) - } - - // Refresh token will be missing, but this access_token will be good - // for one hour. - fmt.Printf("access_token = %v\n", o.AccessToken) - fmt.Printf("refresh_token = %v\n", o.RefreshToken) - fmt.Printf("expires %v\n", o.Expiry) - - // Form the request to list Google Cloud Storage buckets. - req, err := http.NewRequest("GET", "https://storage.googleapis.com/", nil) - if err != nil { - log.Fatal("http.NewRequest:", err) - } - req.Header.Set("Authorization", "OAuth "+o.AccessToken) - req.Header.Set("x-goog-api-version", "2") - req.Header.Set("x-goog-project-id", projectID) - - // Make the request. - r, err := c.Do(req) - if err != nil { - log.Fatal("API request error:", err) - } - defer r.Body.Close() - - // Write the response to standard output. - res, err := ioutil.ReadAll(r.Body) - if err != nil { - log.Fatal("error reading API request results:", err) - } - fmt.Printf("\nRESULT:\n%s\n", res) -} diff --git a/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/jwt/jwt.go b/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/jwt/jwt.go deleted file mode 100644 index 61bf5ce..0000000 --- a/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/jwt/jwt.go +++ /dev/null @@ -1,511 +0,0 @@ -// Copyright 2012 The goauth2 Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// The jwt package provides support for creating credentials for OAuth2 service -// account requests. -// -// For examples of the package usage please see jwt_test.go. -// Example usage (error handling omitted for brevity): -// -// // Craft the ClaimSet and JWT token. -// iss := "XXXXXXXXXXXX@developer.gserviceaccount.com" -// scope := "https://www.googleapis.com/auth/devstorage.read_only" -// t := jwt.NewToken(iss, scope, pemKeyBytes) -// -// // We need to provide a client. -// c := &http.Client{} -// -// // Get the access token. -// o, _ := t.Assert(c) -// -// // Form the request to the service. -// req, _ := http.NewRequest("GET", "https://storage.googleapis.com/", nil) -// req.Header.Set("Authorization", "OAuth "+o.AccessToken) -// req.Header.Set("x-goog-api-version", "2") -// req.Header.Set("x-goog-project-id", "XXXXXXXXXXXX") -// -// // Make the request. -// result, _ := c.Do(req) -// -// For info on OAuth2 service accounts please see the online documentation. -// https://developers.google.com/accounts/docs/OAuth2ServiceAccount -// -package jwt - -import ( - "bytes" - "crypto" - "crypto/rand" - "crypto/rsa" - "crypto/sha256" - "crypto/x509" - "encoding/base64" - "encoding/json" - "encoding/pem" - "errors" - "fmt" - "net/http" - "net/url" - "strings" - "time" - - "code.google.com/p/goauth2/oauth" -) - -// These are the default/standard values for this to work for Google service accounts. -const ( - stdAlgorithm = "RS256" - stdType = "JWT" - stdAssertionType = "http://oauth.net/grant_type/jwt/1.0/bearer" - stdGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer" - stdAud = "https://accounts.google.com/o/oauth2/token" -) - -var ( - ErrInvalidKey = errors.New("Invalid Key") -) - -// base64Encode returns and Base64url encoded version of the input string with any -// trailing "=" stripped. -func base64Encode(b []byte) string { - return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") -} - -// base64Decode decodes the Base64url encoded string -func base64Decode(s string) ([]byte, error) { - // add back missing padding - switch len(s) % 4 { - case 2: - s += "==" - case 3: - s += "=" - } - return base64.URLEncoding.DecodeString(s) -} - -// The JWT claim set contains information about the JWT including the -// permissions being requested (scopes), the target of the token, the issuer, -// the time the token was issued, and the lifetime of the token. -// -// Aud is usually https://accounts.google.com/o/oauth2/token -type ClaimSet struct { - Iss string `json:"iss"` // email address of the client_id of the application making the access token request - Scope string `json:"scope,omitempty"` // space-delimited list of the permissions the application requests - Aud string `json:"aud"` // descriptor of the intended target of the assertion (Optional). - Prn string `json:"prn,omitempty"` // email for which the application is requesting delegated access (Optional). - Exp int64 `json:"exp"` - Iat int64 `json:"iat"` - Typ string `json:"typ,omitempty"` - Sub string `json:"sub,omitempty"` // Add support for googleapi delegation support - - // See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3 - // This array is marshalled using custom code (see (c *ClaimSet) encode()). - PrivateClaims map[string]interface{} `json:"-"` - - exp time.Time - iat time.Time -} - -// setTimes sets iat and exp to time.Now() and iat.Add(time.Hour) respectively. -// -// Note that these times have nothing to do with the expiration time for the -// access_token returned by the server. These have to do with the lifetime of -// the encoded JWT. -// -// A JWT can be re-used for up to one hour after it was encoded. The access -// token that is granted will also be good for one hour so there is little point -// in trying to use the JWT a second time. -func (c *ClaimSet) setTimes(t time.Time) { - c.iat = t - c.exp = c.iat.Add(time.Hour) -} - -var ( - jsonStart = []byte{'{'} - jsonEnd = []byte{'}'} -) - -// encode returns the Base64url encoded form of the Signature. -func (c *ClaimSet) encode() string { - if c.exp.IsZero() || c.iat.IsZero() { - c.setTimes(time.Now()) - } - if c.Aud == "" { - c.Aud = stdAud - } - c.Exp = c.exp.Unix() - c.Iat = c.iat.Unix() - - b, err := json.Marshal(c) - if err != nil { - panic(err) - } - - if len(c.PrivateClaims) == 0 { - return base64Encode(b) - } - - // Marshal private claim set and then append it to b. - prv, err := json.Marshal(c.PrivateClaims) - if err != nil { - panic(fmt.Errorf("Invalid map of private claims %v", c.PrivateClaims)) - } - - // Concatenate public and private claim JSON objects. - if !bytes.HasSuffix(b, jsonEnd) { - panic(fmt.Errorf("Invalid JSON %s", b)) - } - if !bytes.HasPrefix(prv, jsonStart) { - panic(fmt.Errorf("Invalid JSON %s", prv)) - } - b[len(b)-1] = ',' // Replace closing curly brace with a comma. - b = append(b, prv[1:]...) // Append private claims. - - return base64Encode(b) -} - -// Header describes the algorithm and type of token being generated, -// and optionally a KeyID describing additional parameters for the -// signature. -type Header struct { - Algorithm string `json:"alg"` - Type string `json:"typ"` - KeyId string `json:"kid,omitempty"` -} - -func (h *Header) encode() string { - b, err := json.Marshal(h) - if err != nil { - panic(err) - } - return base64Encode(b) -} - -// A JWT is composed of three parts: a header, a claim set, and a signature. -// The well formed and encoded JWT can then be exchanged for an access token. -// -// The Token is not a JWT, but is is encoded to produce a well formed JWT. -// -// When obtaining a key from the Google API console it will be downloaded in a -// PKCS12 encoding. To use this key you will need to convert it to a PEM file. -// This can be achieved with openssl. -// -// $ openssl pkcs12 -in -nocerts -passin pass:notasecret -nodes -out -// -// The contents of this file can then be used as the Key. -type Token struct { - ClaimSet *ClaimSet // claim set used to construct the JWT - Header *Header // header used to construct the JWT - Key []byte // PEM printable encoding of the private key - pKey *rsa.PrivateKey - - header string - claim string - sig string - - useExternalSigner bool - signer Signer -} - -// NewToken returns a filled in *Token based on the standard header, -// and sets the Iat and Exp times based on when the call to Assert is -// made. -func NewToken(iss, scope string, key []byte) *Token { - c := &ClaimSet{ - Iss: iss, - Scope: scope, - Aud: stdAud, - } - h := &Header{ - Algorithm: stdAlgorithm, - Type: stdType, - } - t := &Token{ - ClaimSet: c, - Header: h, - Key: key, - } - return t -} - -// Signer is an interface that given a JWT token, returns the header & -// claim (serialized and urlEncoded to a byte slice), along with the -// signature and an error (if any occured). It could modify any data -// to sign (typically the KeyID). -// -// Example usage where a SHA256 hash of the original url-encoded token -// with an added KeyID and secret data is used as a signature: -// -// var privateData = "secret data added to hash, indexed by KeyID" -// -// type SigningService struct{} -// -// func (ss *SigningService) Sign(in *jwt.Token) (newTokenData, sig []byte, err error) { -// in.Header.KeyID = "signing service" -// newTokenData = in.EncodeWithoutSignature() -// dataToSign := fmt.Sprintf("%s.%s", newTokenData, privateData) -// h := sha256.New() -// _, err := h.Write([]byte(dataToSign)) -// sig = h.Sum(nil) -// return -// } -type Signer interface { - Sign(in *Token) (tokenData, signature []byte, err error) -} - -// NewSignerToken returns a *Token, using an external signer function -func NewSignerToken(iss, scope string, signer Signer) *Token { - t := NewToken(iss, scope, nil) - t.useExternalSigner = true - t.signer = signer - return t -} - -// Expired returns a boolean value letting us know if the token has expired. -func (t *Token) Expired() bool { - return t.ClaimSet.exp.Before(time.Now()) -} - -// Encode constructs and signs a Token returning a JWT ready to use for -// requesting an access token. -func (t *Token) Encode() (string, error) { - var tok string - t.header = t.Header.encode() - t.claim = t.ClaimSet.encode() - err := t.sign() - if err != nil { - return tok, err - } - tok = fmt.Sprintf("%s.%s.%s", t.header, t.claim, t.sig) - return tok, nil -} - -// EncodeWithoutSignature returns the url-encoded value of the Token -// before signing has occured (typically for use by external signers). -func (t *Token) EncodeWithoutSignature() string { - t.header = t.Header.encode() - t.claim = t.ClaimSet.encode() - return fmt.Sprintf("%s.%s", t.header, t.claim) -} - -// sign computes the signature for a Token. The details for this can be found -// in the OAuth2 Service Account documentation. -// https://developers.google.com/accounts/docs/OAuth2ServiceAccount#computingsignature -func (t *Token) sign() error { - if t.useExternalSigner { - fulldata, sig, err := t.signer.Sign(t) - if err != nil { - return err - } - split := strings.Split(string(fulldata), ".") - if len(split) != 2 { - return errors.New("no token returned") - } - t.header = split[0] - t.claim = split[1] - t.sig = base64Encode(sig) - return err - } - ss := fmt.Sprintf("%s.%s", t.header, t.claim) - if t.pKey == nil { - err := t.parsePrivateKey() - if err != nil { - return err - } - } - h := sha256.New() - h.Write([]byte(ss)) - b, err := rsa.SignPKCS1v15(rand.Reader, t.pKey, crypto.SHA256, h.Sum(nil)) - t.sig = base64Encode(b) - return err -} - -// parsePrivateKey converts the Token's Key ([]byte) into a parsed -// rsa.PrivateKey. If the key is not well formed this method will return an -// ErrInvalidKey error. -func (t *Token) parsePrivateKey() error { - block, _ := pem.Decode(t.Key) - if block == nil { - return ErrInvalidKey - } - parsedKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) - if err != nil { - parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes) - if err != nil { - return err - } - } - var ok bool - t.pKey, ok = parsedKey.(*rsa.PrivateKey) - if !ok { - return ErrInvalidKey - } - return nil -} - -// Assert obtains an *oauth.Token from the remote server by encoding and sending -// a JWT. The access_token will expire in one hour (3600 seconds) and cannot be -// refreshed (no refresh_token is returned with the response). Once this token -// expires call this method again to get a fresh one. -func (t *Token) Assert(c *http.Client) (*oauth.Token, error) { - var o *oauth.Token - t.ClaimSet.setTimes(time.Now()) - u, v, err := t.buildRequest() - if err != nil { - return o, err - } - resp, err := c.PostForm(u, v) - if err != nil { - return o, err - } - o, err = handleResponse(resp) - return o, err -} - -// buildRequest sets up the URL values and the proper URL string for making our -// access_token request. -func (t *Token) buildRequest() (string, url.Values, error) { - v := url.Values{} - j, err := t.Encode() - if err != nil { - return t.ClaimSet.Aud, v, err - } - v.Set("grant_type", stdGrantType) - v.Set("assertion", j) - return t.ClaimSet.Aud, v, nil -} - -// Used for decoding the response body. -type respBody struct { - IdToken string `json:"id_token"` - Access string `json:"access_token"` - Type string `json:"token_type"` - ExpiresIn time.Duration `json:"expires_in"` -} - -// handleResponse returns a filled in *oauth.Token given the *http.Response from -// a *http.Request created by buildRequest. -func handleResponse(r *http.Response) (*oauth.Token, error) { - o := &oauth.Token{} - defer r.Body.Close() - if r.StatusCode != 200 { - return o, errors.New("invalid response: " + r.Status) - } - b := &respBody{} - err := json.NewDecoder(r.Body).Decode(b) - if err != nil { - return o, err - } - o.AccessToken = b.Access - if b.IdToken != "" { - // decode returned id token to get expiry - o.AccessToken = b.IdToken - s := strings.Split(b.IdToken, ".") - if len(s) < 2 { - return nil, errors.New("invalid token received") - } - d, err := base64Decode(s[1]) - if err != nil { - return o, err - } - c := &ClaimSet{} - err = json.NewDecoder(bytes.NewBuffer(d)).Decode(c) - if err != nil { - return o, err - } - o.Expiry = time.Unix(c.Exp, 0) - return o, nil - } - o.Expiry = time.Now().Add(b.ExpiresIn * time.Second) - return o, nil -} - -// Transport implements http.RoundTripper. When configured with a valid -// JWT and OAuth tokens it can be used to make authenticated HTTP requests. -// -// t := &jwt.Transport{jwtToken, oauthToken} -// r, _, err := t.Client().Get("http://example.org/url/requiring/auth") -// -// It will automatically refresh the OAuth token if it can, updating in place. -type Transport struct { - JWTToken *Token - OAuthToken *oauth.Token - - // Transport is the HTTP transport to use when making requests. - // It will default to http.DefaultTransport if nil. - Transport http.RoundTripper -} - -// Creates a new authenticated transport. -func NewTransport(token *Token) (*Transport, error) { - oa, err := token.Assert(new(http.Client)) - if err != nil { - return nil, err - } - return &Transport{ - JWTToken: token, - OAuthToken: oa, - }, nil -} - -// Client returns an *http.Client that makes OAuth-authenticated requests. -func (t *Transport) Client() *http.Client { - return &http.Client{Transport: t} -} - -// Fetches the internal transport. -func (t *Transport) transport() http.RoundTripper { - if t.Transport != nil { - return t.Transport - } - return http.DefaultTransport -} - -// RoundTrip executes a single HTTP transaction using the Transport's -// OAuthToken as authorization headers. -// -// This method will attempt to renew the token if it has expired and may return -// an error related to that token renewal before attempting the client request. -// If the token cannot be renewed a non-nil os.Error value will be returned. -// If the token is invalid callers should expect HTTP-level errors, -// as indicated by the Response's StatusCode. -func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { - // Sanity check the two tokens - if t.JWTToken == nil { - return nil, fmt.Errorf("no JWT token supplied") - } - if t.OAuthToken == nil { - return nil, fmt.Errorf("no OAuth token supplied") - } - // Refresh the OAuth token if it has expired - if t.OAuthToken.Expired() { - if oa, err := t.JWTToken.Assert(new(http.Client)); err != nil { - return nil, err - } else { - t.OAuthToken = oa - } - } - // To set the Authorization header, we must make a copy of the Request - // so that we don't modify the Request we were given. - // This is required by the specification of http.RoundTripper. - req = cloneRequest(req) - req.Header.Set("Authorization", "Bearer "+t.OAuthToken.AccessToken) - - // Make the HTTP request. - return t.transport().RoundTrip(req) -} - -// cloneRequest returns a clone of the provided *http.Request. -// The clone is a shallow copy of the struct and its Header map. -func cloneRequest(r *http.Request) *http.Request { - // shallow copy of the struct - r2 := new(http.Request) - *r2 = *r - // deep copy of the Header - r2.Header = make(http.Header) - for k, s := range r.Header { - r2.Header[k] = s - } - return r2 -} diff --git a/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/oauth.go b/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/oauth.go deleted file mode 100644 index fc968ae..0000000 --- a/Godeps/_workspace/src/code.google.com/p/goauth2/oauth/oauth.go +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright 2011 The goauth2 Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package oauth supports making OAuth2-authenticated HTTP requests. -// -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// !!! DEPRECATED. Use golang.org/x/oauth2 instead. !!! -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// -// Example usage: -// -// // Specify your configuration. (typically as a global variable) -// var config = &oauth.Config{ -// ClientId: YOUR_CLIENT_ID, -// ClientSecret: YOUR_CLIENT_SECRET, -// Scope: "https://www.googleapis.com/auth/buzz", -// AuthURL: "https://accounts.google.com/o/oauth2/auth", -// TokenURL: "https://accounts.google.com/o/oauth2/token", -// RedirectURL: "http://you.example.org/handler", -// } -// -// // A landing page redirects to the OAuth provider to get the auth code. -// func landing(w http.ResponseWriter, r *http.Request) { -// http.Redirect(w, r, config.AuthCodeURL("foo"), http.StatusFound) -// } -// -// // The user will be redirected back to this handler, that takes the -// // "code" query parameter and Exchanges it for an access token. -// func handler(w http.ResponseWriter, r *http.Request) { -// t := &oauth.Transport{Config: config} -// t.Exchange(r.FormValue("code")) -// // The Transport now has a valid Token. Create an *http.Client -// // with which we can make authenticated API requests. -// c := t.Client() -// c.Post(...) -// // ... -// // btw, r.FormValue("state") == "foo" -// } -// -package oauth - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "mime" - "net/http" - "net/url" - "os" - "strconv" - "strings" - "sync" - "time" -) - -// OAuthError is the error type returned by many operations. -// -// In retrospect it should not exist. Don't depend on it. -type OAuthError struct { - prefix string - msg string -} - -func (oe OAuthError) Error() string { - return "OAuthError: " + oe.prefix + ": " + oe.msg -} - -// Cache specifies the methods that implement a Token cache. -type Cache interface { - Token() (*Token, error) - PutToken(*Token) error -} - -// CacheFile implements Cache. Its value is the name of the file in which -// the Token is stored in JSON format. -type CacheFile string - -func (f CacheFile) Token() (*Token, error) { - file, err := os.Open(string(f)) - if err != nil { - return nil, OAuthError{"CacheFile.Token", err.Error()} - } - defer file.Close() - tok := &Token{} - if err := json.NewDecoder(file).Decode(tok); err != nil { - return nil, OAuthError{"CacheFile.Token", err.Error()} - } - return tok, nil -} - -func (f CacheFile) PutToken(tok *Token) error { - file, err := os.OpenFile(string(f), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - return OAuthError{"CacheFile.PutToken", err.Error()} - } - if err := json.NewEncoder(file).Encode(tok); err != nil { - file.Close() - return OAuthError{"CacheFile.PutToken", err.Error()} - } - if err := file.Close(); err != nil { - return OAuthError{"CacheFile.PutToken", err.Error()} - } - return nil -} - -// Config is the configuration of an OAuth consumer. -type Config struct { - // ClientId is the OAuth client identifier used when communicating with - // the configured OAuth provider. - ClientId string - - // ClientSecret is the OAuth client secret used when communicating with - // the configured OAuth provider. - ClientSecret string - - // Scope identifies the level of access being requested. Multiple scope - // values should be provided as a space-delimited string. - Scope string - - // AuthURL is the URL the user will be directed to in order to grant - // access. - AuthURL string - - // TokenURL is the URL used to retrieve OAuth tokens. - TokenURL string - - // RedirectURL is the URL to which the user will be returned after - // granting (or denying) access. - RedirectURL string - - // TokenCache allows tokens to be cached for subsequent requests. - TokenCache Cache - - // AccessType is an OAuth extension that gets sent as the - // "access_type" field in the URL from AuthCodeURL. - // See https://developers.google.com/accounts/docs/OAuth2WebServer. - // It may be "online" (the default) or "offline". - // If your application needs to refresh access tokens when the - // user is not present at the browser, then use offline. This - // will result in your application obtaining a refresh token - // the first time your application exchanges an authorization - // code for a user. - AccessType string - - // ApprovalPrompt indicates whether the user should be - // re-prompted for consent. If set to "auto" (default) the - // user will be prompted only if they haven't previously - // granted consent and the code can only be exchanged for an - // access token. - // If set to "force" the user will always be prompted, and the - // code can be exchanged for a refresh token. - ApprovalPrompt string -} - -// Token contains an end-user's tokens. -// This is the data you must store to persist authentication. -type Token struct { - AccessToken string - RefreshToken string - Expiry time.Time // If zero the token has no (known) expiry time. - - // Extra optionally contains extra metadata from the server - // when updating a token. The only current key that may be - // populated is "id_token". It may be nil and will be - // initialized as needed. - Extra map[string]string -} - -// Expired reports whether the token has expired or is invalid. -func (t *Token) Expired() bool { - if t.AccessToken == "" { - return true - } - if t.Expiry.IsZero() { - return false - } - return t.Expiry.Before(time.Now()) -} - -// Transport implements http.RoundTripper. When configured with a valid -// Config and Token it can be used to make authenticated HTTP requests. -// -// t := &oauth.Transport{config} -// t.Exchange(code) -// // t now contains a valid Token -// r, _, err := t.Client().Get("http://example.org/url/requiring/auth") -// -// It will automatically refresh the Token if it can, -// updating the supplied Token in place. -type Transport struct { - *Config - *Token - - // mu guards modifying the token. - mu sync.Mutex - - // Transport is the HTTP transport to use when making requests. - // It will default to http.DefaultTransport if nil. - // (It should never be an oauth.Transport.) - Transport http.RoundTripper -} - -// Client returns an *http.Client that makes OAuth-authenticated requests. -func (t *Transport) Client() *http.Client { - return &http.Client{Transport: t} -} - -func (t *Transport) transport() http.RoundTripper { - if t.Transport != nil { - return t.Transport - } - return http.DefaultTransport -} - -// AuthCodeURL returns a URL that the end-user should be redirected to, -// so that they may obtain an authorization code. -func (c *Config) AuthCodeURL(state string) string { - url_, err := url.Parse(c.AuthURL) - if err != nil { - panic("AuthURL malformed: " + err.Error()) - } - q := url.Values{ - "response_type": {"code"}, - "client_id": {c.ClientId}, - "state": condVal(state), - "scope": condVal(c.Scope), - "redirect_uri": condVal(c.RedirectURL), - "access_type": condVal(c.AccessType), - "approval_prompt": condVal(c.ApprovalPrompt), - }.Encode() - if url_.RawQuery == "" { - url_.RawQuery = q - } else { - url_.RawQuery += "&" + q - } - return url_.String() -} - -func condVal(v string) []string { - if v == "" { - return nil - } - return []string{v} -} - -// Exchange takes a code and gets access Token from the remote server. -func (t *Transport) Exchange(code string) (*Token, error) { - if t.Config == nil { - return nil, OAuthError{"Exchange", "no Config supplied"} - } - - // If the transport or the cache already has a token, it is - // passed to `updateToken` to preserve existing refresh token. - tok := t.Token - if tok == nil && t.TokenCache != nil { - tok, _ = t.TokenCache.Token() - } - if tok == nil { - tok = new(Token) - } - err := t.updateToken(tok, url.Values{ - "grant_type": {"authorization_code"}, - "redirect_uri": {t.RedirectURL}, - "scope": {t.Scope}, - "code": {code}, - }) - if err != nil { - return nil, err - } - t.Token = tok - if t.TokenCache != nil { - return tok, t.TokenCache.PutToken(tok) - } - return tok, nil -} - -// RoundTrip executes a single HTTP transaction using the Transport's -// Token as authorization headers. -// -// This method will attempt to renew the Token if it has expired and may return -// an error related to that Token renewal before attempting the client request. -// If the Token cannot be renewed a non-nil os.Error value will be returned. -// If the Token is invalid callers should expect HTTP-level errors, -// as indicated by the Response's StatusCode. -func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { - accessToken, err := t.getAccessToken() - if err != nil { - return nil, err - } - // To set the Authorization header, we must make a copy of the Request - // so that we don't modify the Request we were given. - // This is required by the specification of http.RoundTripper. - req = cloneRequest(req) - req.Header.Set("Authorization", "Bearer "+accessToken) - - // Make the HTTP request. - return t.transport().RoundTrip(req) -} - -func (t *Transport) getAccessToken() (string, error) { - t.mu.Lock() - defer t.mu.Unlock() - - if t.Token == nil { - if t.Config == nil { - return "", OAuthError{"RoundTrip", "no Config supplied"} - } - if t.TokenCache == nil { - return "", OAuthError{"RoundTrip", "no Token supplied"} - } - var err error - t.Token, err = t.TokenCache.Token() - if err != nil { - return "", err - } - } - - // Refresh the Token if it has expired. - if t.Expired() { - if err := t.Refresh(); err != nil { - return "", err - } - } - if t.AccessToken == "" { - return "", errors.New("no access token obtained from refresh") - } - return t.AccessToken, nil -} - -// cloneRequest returns a clone of the provided *http.Request. -// The clone is a shallow copy of the struct and its Header map. -func cloneRequest(r *http.Request) *http.Request { - // shallow copy of the struct - r2 := new(http.Request) - *r2 = *r - // deep copy of the Header - r2.Header = make(http.Header) - for k, s := range r.Header { - r2.Header[k] = s - } - return r2 -} - -// Refresh renews the Transport's AccessToken using its RefreshToken. -func (t *Transport) Refresh() error { - if t.Token == nil { - return OAuthError{"Refresh", "no existing Token"} - } - if t.RefreshToken == "" { - return OAuthError{"Refresh", "Token expired; no Refresh Token"} - } - if t.Config == nil { - return OAuthError{"Refresh", "no Config supplied"} - } - - err := t.updateToken(t.Token, url.Values{ - "grant_type": {"refresh_token"}, - "refresh_token": {t.RefreshToken}, - }) - if err != nil { - return err - } - if t.TokenCache != nil { - return t.TokenCache.PutToken(t.Token) - } - return nil -} - -// AuthenticateClient gets an access Token using the client_credentials grant -// type. -func (t *Transport) AuthenticateClient() error { - if t.Config == nil { - return OAuthError{"Exchange", "no Config supplied"} - } - if t.Token == nil { - t.Token = &Token{} - } - return t.updateToken(t.Token, url.Values{"grant_type": {"client_credentials"}}) -} - -// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL -// implements the OAuth2 spec correctly -// See https://code.google.com/p/goauth2/issues/detail?id=31 for background. -// In summary: -// - Reddit only accepts client secret in the Authorization header -// - Dropbox accepts either it in URL param or Auth header, but not both. -// - Google only accepts URL param (not spec compliant?), not Auth header -func providerAuthHeaderWorks(tokenURL string) bool { - if strings.HasPrefix(tokenURL, "https://accounts.google.com/") || - strings.HasPrefix(tokenURL, "https://github.com/") || - strings.HasPrefix(tokenURL, "https://api.instagram.com/") || - strings.HasPrefix(tokenURL, "https://www.douban.com/") { - // Some sites fail to implement the OAuth2 spec fully. - return false - } - - // Assume the provider implements the spec properly - // otherwise. We can add more exceptions as they're - // discovered. We will _not_ be adding configurable hooks - // to this package to let users select server bugs. - return true -} - -// updateToken mutates both tok and v. -func (t *Transport) updateToken(tok *Token, v url.Values) error { - v.Set("client_id", t.ClientId) - bustedAuth := !providerAuthHeaderWorks(t.TokenURL) - if bustedAuth { - v.Set("client_secret", t.ClientSecret) - } - client := &http.Client{Transport: t.transport()} - req, err := http.NewRequest("POST", t.TokenURL, strings.NewReader(v.Encode())) - if err != nil { - return err - } - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - if !bustedAuth { - req.SetBasicAuth(t.ClientId, t.ClientSecret) - } - r, err := client.Do(req) - if err != nil { - return err - } - defer r.Body.Close() - if r.StatusCode != 200 { - return OAuthError{"updateToken", "Unexpected HTTP status " + r.Status} - } - var b struct { - Access string `json:"access_token"` - Refresh string `json:"refresh_token"` - ExpiresIn int64 `json:"expires_in"` // seconds - Id string `json:"id_token"` - } - - body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20)) - if err != nil { - return err - } - - content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type")) - switch content { - case "application/x-www-form-urlencoded", "text/plain": - vals, err := url.ParseQuery(string(body)) - if err != nil { - return err - } - - b.Access = vals.Get("access_token") - b.Refresh = vals.Get("refresh_token") - b.ExpiresIn, _ = strconv.ParseInt(vals.Get("expires_in"), 10, 64) - b.Id = vals.Get("id_token") - default: - if err = json.Unmarshal(body, &b); err != nil { - return fmt.Errorf("got bad response from server: %q", body) - } - } - if b.Access == "" { - return errors.New("received empty access token from authorization server") - } - tok.AccessToken = b.Access - // Don't overwrite `RefreshToken` with an empty value - if b.Refresh != "" { - tok.RefreshToken = b.Refresh - } - if b.ExpiresIn == 0 { - tok.Expiry = time.Time{} - } else { - tok.Expiry = time.Now().Add(time.Duration(b.ExpiresIn) * time.Second) - } - if b.Id != "" { - if tok.Extra == nil { - tok.Extra = make(map[string]string) - } - tok.Extra["id_token"] = b.Id - } - return nil -} diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go index 84bbaf4..e98ddec 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go @@ -30,7 +30,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Protocol buffer deep copy and merge. -// TODO: MessageSet and RawMessage. +// TODO: RawMessage. package proto diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go index 7321e1a..231b074 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go @@ -105,6 +105,11 @@ func (p *Buffer) EncodeVarint(x uint64) error { return nil } +// SizeVarint returns the varint encoding size of an integer. +func SizeVarint(x uint64) int { + return sizeVarint(x) +} + func sizeVarint(x uint64) (n int) { for { n++ @@ -1248,24 +1253,9 @@ func size_struct(prop *StructProperties, base structPointer) (n int) { } // Factor in any oneof fields. - // TODO: This could be faster and use less reflection. - if prop.oneofMarshaler != nil { - sv := reflect.ValueOf(structPointer_Interface(base, prop.stype)).Elem() - for i := 0; i < prop.stype.NumField(); i++ { - fv := sv.Field(i) - if fv.Kind() != reflect.Interface || fv.IsNil() { - continue - } - if prop.stype.Field(i).Tag.Get("protobuf_oneof") == "" { - continue - } - spv := fv.Elem() // interface -> *T - sv := spv.Elem() // *T -> T - sf := sv.Type().Field(0) // StructField inside T - var prop Properties - prop.Init(sf.Type, "whatever", sf.Tag.Get("protobuf"), &sf) - n += prop.size(&prop, toStructPointer(spv)) - } + if prop.oneofSizer != nil { + m := structPointer_Interface(base, prop.stype).(Message) + n += prop.oneofSizer(m) } return diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go index 5475c3d..f5db1de 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go @@ -30,7 +30,6 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Protocol buffer comparison. -// TODO: MessageSet. package proto @@ -51,7 +50,9 @@ Equality is defined in this way: are equal, and extensions sets are equal. - Two set scalar fields are equal iff their values are equal. If the fields are of a floating-point type, remember that - NaN != x for all x, including NaN. + NaN != x for all x, including NaN. If the message is defined + in a proto3 .proto file, fields are not "set"; specifically, + zero length proto3 "bytes" fields are equal (nil == {}). - Two repeated fields are equal iff their lengths are the same, and their corresponding elements are equal (a "bytes" field, although represented by []byte, is not a repeated field) @@ -89,6 +90,7 @@ func Equal(a, b Message) bool { // v1 and v2 are known to have the same type. func equalStruct(v1, v2 reflect.Value) bool { + sprop := GetProperties(v1.Type()) for i := 0; i < v1.NumField(); i++ { f := v1.Type().Field(i) if strings.HasPrefix(f.Name, "XXX_") { @@ -114,7 +116,7 @@ func equalStruct(v1, v2 reflect.Value) bool { } f1, f2 = f1.Elem(), f2.Elem() } - if !equalAny(f1, f2) { + if !equalAny(f1, f2, sprop.Prop[i]) { return false } } @@ -141,7 +143,8 @@ func equalStruct(v1, v2 reflect.Value) bool { } // v1 and v2 are known to have the same type. -func equalAny(v1, v2 reflect.Value) bool { +// prop may be nil. +func equalAny(v1, v2 reflect.Value, prop *Properties) bool { if v1.Type() == protoMessageType { m1, _ := v1.Interface().(Message) m2, _ := v2.Interface().(Message) @@ -164,7 +167,7 @@ func equalAny(v1, v2 reflect.Value) bool { if e1.Type() != e2.Type() { return false } - return equalAny(e1, e2) + return equalAny(e1, e2, nil) case reflect.Map: if v1.Len() != v2.Len() { return false @@ -175,16 +178,22 @@ func equalAny(v1, v2 reflect.Value) bool { // This key was not found in the second map. return false } - if !equalAny(v1.MapIndex(key), val2) { + if !equalAny(v1.MapIndex(key), val2, nil) { return false } } return true case reflect.Ptr: - return equalAny(v1.Elem(), v2.Elem()) + return equalAny(v1.Elem(), v2.Elem(), prop) case reflect.Slice: if v1.Type().Elem().Kind() == reflect.Uint8 { // short circuit: []byte + + // Edge case: if this is in a proto3 message, a zero length + // bytes field is considered the zero value. + if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 { + return true + } if v1.IsNil() != v2.IsNil() { return false } @@ -195,7 +204,7 @@ func equalAny(v1, v2 reflect.Value) bool { return false } for i := 0; i < v1.Len(); i++ { - if !equalAny(v1.Index(i), v2.Index(i)) { + if !equalAny(v1.Index(i), v2.Index(i), prop) { return false } } @@ -230,7 +239,7 @@ func equalExtensions(base reflect.Type, em1, em2 map[int32]Extension) bool { if m1 != nil && m2 != nil { // Both are unencoded. - if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2)) { + if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) { return false } continue @@ -258,7 +267,7 @@ func equalExtensions(base reflect.Type, em1, em2 map[int32]Extension) bool { log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err) return false } - if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2)) { + if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) { return false } } diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go index e591cce..054f4f1 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go @@ -301,7 +301,6 @@ func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) { o := NewBuffer(b) t := reflect.TypeOf(extension.ExtensionType) - rep := extension.repeated() props := extensionProperties(extension) @@ -323,7 +322,7 @@ func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) { return nil, err } - if !rep || o.index >= len(o.buf) { + if o.index >= len(o.buf) { break } } diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go index dcabe3b..0de8f8d 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go @@ -70,6 +70,12 @@ for a protocol buffer variable v: with distinguished wrapper types for each possible field value. - Marshal and Unmarshal are functions to encode and decode the wire format. +When the .proto file specifies `syntax="proto3"`, there are some differences: + + - Non-repeated fields of non-message type are values instead of pointers. + - Getters are only generated for message and oneof fields. + - Enum types do not get an Enum method. + The simplest way to describe this is to see an example. Given file test.proto, containing @@ -229,6 +235,7 @@ To create and play with a Test object: test := &pb.Test{ Label: proto.String("hello"), Type: proto.Int32(17), + Reps: []int64{1, 2, 3}, Optionalgroup: &pb.Test_OptionalGroup{ RequiredField: proto.String("good bye"), }, @@ -881,3 +888,7 @@ func isProto3Zero(v reflect.Value) bool { } return false } + +// ProtoPackageIsVersion1 is referenced from generated protocol buffer files +// to assert that that code is compatible with this version of the proto package. +const ProtoPackageIsVersion1 = true diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go index 9d912bc..e25e01e 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go @@ -44,11 +44,11 @@ import ( "sort" ) -// ErrNoMessageTypeId occurs when a protocol buffer does not have a message type ID. +// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID. // A message type ID is required for storing a protocol buffer in a message set. -var ErrNoMessageTypeId = errors.New("proto does not have a message type ID") +var errNoMessageTypeID = errors.New("proto does not have a message type ID") -// The first two types (_MessageSet_Item and MessageSet) +// The first two types (_MessageSet_Item and messageSet) // model what the protocol compiler produces for the following protocol message: // message MessageSet { // repeated group Item = 1 { @@ -58,27 +58,20 @@ var ErrNoMessageTypeId = errors.New("proto does not have a message type ID") // } // That is the MessageSet wire format. We can't use a proto to generate these // because that would introduce a circular dependency between it and this package. -// -// When a proto1 proto has a field that looks like: -// optional message info = 3; -// the protocol compiler produces a field in the generated struct that looks like: -// Info *_proto_.MessageSet `protobuf:"bytes,3,opt,name=info"` -// The package is automatically inserted so there is no need for that proto file to -// import this package. type _MessageSet_Item struct { TypeId *int32 `protobuf:"varint,2,req,name=type_id"` Message []byte `protobuf:"bytes,3,req,name=message"` } -type MessageSet struct { +type messageSet struct { Item []*_MessageSet_Item `protobuf:"group,1,rep"` XXX_unrecognized []byte // TODO: caching? } -// Make sure MessageSet is a Message. -var _ Message = (*MessageSet)(nil) +// Make sure messageSet is a Message. +var _ Message = (*messageSet)(nil) // messageTypeIder is an interface satisfied by a protocol buffer type // that may be stored in a MessageSet. @@ -86,7 +79,7 @@ type messageTypeIder interface { MessageTypeId() int32 } -func (ms *MessageSet) find(pb Message) *_MessageSet_Item { +func (ms *messageSet) find(pb Message) *_MessageSet_Item { mti, ok := pb.(messageTypeIder) if !ok { return nil @@ -100,24 +93,24 @@ func (ms *MessageSet) find(pb Message) *_MessageSet_Item { return nil } -func (ms *MessageSet) Has(pb Message) bool { +func (ms *messageSet) Has(pb Message) bool { if ms.find(pb) != nil { return true } return false } -func (ms *MessageSet) Unmarshal(pb Message) error { +func (ms *messageSet) Unmarshal(pb Message) error { if item := ms.find(pb); item != nil { return Unmarshal(item.Message, pb) } if _, ok := pb.(messageTypeIder); !ok { - return ErrNoMessageTypeId + return errNoMessageTypeID } return nil // TODO: return error instead? } -func (ms *MessageSet) Marshal(pb Message) error { +func (ms *messageSet) Marshal(pb Message) error { msg, err := Marshal(pb) if err != nil { return err @@ -130,7 +123,7 @@ func (ms *MessageSet) Marshal(pb Message) error { mti, ok := pb.(messageTypeIder) if !ok { - return ErrNoMessageTypeId + return errNoMessageTypeID } mtid := mti.MessageTypeId() @@ -141,9 +134,9 @@ func (ms *MessageSet) Marshal(pb Message) error { return nil } -func (ms *MessageSet) Reset() { *ms = MessageSet{} } -func (ms *MessageSet) String() string { return CompactTextString(ms) } -func (*MessageSet) ProtoMessage() {} +func (ms *messageSet) Reset() { *ms = messageSet{} } +func (ms *messageSet) String() string { return CompactTextString(ms) } +func (*messageSet) ProtoMessage() {} // Support for the message_set_wire_format message option. @@ -169,7 +162,7 @@ func MarshalMessageSet(m map[int32]Extension) ([]byte, error) { } sort.Ints(ids) - ms := &MessageSet{Item: make([]*_MessageSet_Item, 0, len(m))} + ms := &messageSet{Item: make([]*_MessageSet_Item, 0, len(m))} for _, id := range ids { e := m[int32(id)] // Remove the wire type and field number varint, as well as the length varint. @@ -186,7 +179,7 @@ func MarshalMessageSet(m map[int32]Extension) ([]byte, error) { // UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format. // It is called by generated Unmarshal methods on protocol buffer messages with the message_set_wire_format option. func UnmarshalMessageSet(buf []byte, m map[int32]Extension) error { - ms := new(MessageSet) + ms := new(messageSet) if err := Unmarshal(buf, ms); err != nil { return err } diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go index b4aaac5..d4531c0 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go @@ -91,6 +91,9 @@ type oneofMarshaler func(Message, *Buffer) error // A oneofUnmarshaler does the unmarshaling for a oneof field in a message. type oneofUnmarshaler func(Message, int, int, *Buffer) (bool, error) +// A oneofSizer does the sizing for all oneof fields in a message. +type oneofSizer func(Message) int + // tagMap is an optimization over map[int]int for typical protocol buffer // use-cases. Encoded protocol buffers are often in tag order with small tag // numbers. @@ -142,6 +145,7 @@ type StructProperties struct { oneofMarshaler oneofMarshaler oneofUnmarshaler oneofUnmarshaler + oneofSizer oneofSizer stype reflect.Type // OneofTypes contains information about the oneof fields in this message. @@ -712,11 +716,11 @@ func getPropertiesLocked(t reflect.Type) *StructProperties { sort.Sort(prop) type oneofMessage interface { - XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), []interface{}) + XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{}) } if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok { var oots []interface{} - prop.oneofMarshaler, prop.oneofUnmarshaler, oots = om.XXX_OneofFuncs() + prop.oneofMarshaler, prop.oneofUnmarshaler, prop.oneofSizer, oots = om.XXX_OneofFuncs() prop.stype = t // Interpret oneof metadata. diff --git a/Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go b/Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go index 81e1bdf..2336b14 100644 --- a/Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go +++ b/Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go @@ -170,20 +170,12 @@ func writeName(w *textWriter, props *Properties) error { return nil } -var ( - messageSetType = reflect.TypeOf((*MessageSet)(nil)).Elem() -) - // raw is the interface satisfied by RawMessage. type raw interface { Bytes() []byte } func writeStruct(w *textWriter, sv reflect.Value) error { - if sv.Type() == messageSetType { - return writeMessageSet(w, sv.Addr().Interface().(*MessageSet)) - } - st := sv.Type() sprops := GetProperties(st) for i := 0; i < sv.NumField(); i++ { @@ -525,44 +517,6 @@ func writeString(w *textWriter, s string) error { return w.WriteByte('"') } -func writeMessageSet(w *textWriter, ms *MessageSet) error { - for _, item := range ms.Item { - id := *item.TypeId - if msd, ok := messageSetMap[id]; ok { - // Known message set type. - if _, err := fmt.Fprintf(w, "[%s]: <\n", msd.name); err != nil { - return err - } - w.indent() - - pb := reflect.New(msd.t.Elem()) - if err := Unmarshal(item.Message, pb.Interface().(Message)); err != nil { - if _, err := fmt.Fprintf(w, "/* bad message: %v */\n", err); err != nil { - return err - } - } else { - if err := writeStruct(w, pb.Elem()); err != nil { - return err - } - } - } else { - // Unknown type. - if _, err := fmt.Fprintf(w, "[%d]: <\n", id); err != nil { - return err - } - w.indent() - if err := writeUnknownStruct(w, item.Message); err != nil { - return err - } - } - w.unindent() - if _, err := w.Write(gtNewline); err != nil { - return err - } - } - return nil -} - func writeUnknownStruct(w *textWriter, data []byte) (err error) { if !w.compact { if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil { diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/LICENSE b/Godeps/_workspace/src/github.com/google/go-github/LICENSE similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/LICENSE rename to Godeps/_workspace/src/github.com/google/go-github/LICENSE diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/activity.go b/Godeps/_workspace/src/github.com/google/go-github/github/activity.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/activity.go rename to Godeps/_workspace/src/github.com/google/go-github/github/activity.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/activity_events.go b/Godeps/_workspace/src/github.com/google/go-github/github/activity_events.go similarity index 98% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/activity_events.go rename to Godeps/_workspace/src/github.com/google/go-github/github/activity_events.go index b8a5e66..2a40d3e 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/activity_events.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/activity_events.go @@ -249,11 +249,11 @@ func (s *ActivityService) ListEventsPerformedByUser(user string, publicOnly bool return *events, resp, err } -// ListEventsRecievedByUser lists the events recieved by a user. If publicOnly is +// ListEventsReceivedByUser lists the events received by a user. If publicOnly is // true, only public events will be returned. // // GitHub API docs: http://developer.github.com/v3/activity/events/#list-events-that-a-user-has-received -func (s *ActivityService) ListEventsRecievedByUser(user string, publicOnly bool, opt *ListOptions) ([]Event, *Response, error) { +func (s *ActivityService) ListEventsReceivedByUser(user string, publicOnly bool, opt *ListOptions) ([]Event, *Response, error) { var u string if publicOnly { u = fmt.Sprintf("users/%v/received_events/public", user) diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/activity_notifications.go b/Godeps/_workspace/src/github.com/google/go-github/github/activity_notifications.go similarity index 99% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/activity_notifications.go rename to Godeps/_workspace/src/github.com/google/go-github/github/activity_notifications.go index 786df98..290b954 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/activity_notifications.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/activity_notifications.go @@ -41,6 +41,7 @@ type NotificationListOptions struct { All bool `url:"all,omitempty"` Participating bool `url:"participating,omitempty"` Since time.Time `url:"since,omitempty"` + Before time.Time `url:"before,omitempty"` } // ListNotifications lists all notifications for the authenticated user. diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/activity_star.go b/Godeps/_workspace/src/github.com/google/go-github/github/activity_star.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/activity_star.go rename to Godeps/_workspace/src/github.com/google/go-github/github/activity_star.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/activity_watching.go b/Godeps/_workspace/src/github.com/google/go-github/github/activity_watching.go similarity index 95% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/activity_watching.go rename to Godeps/_workspace/src/github.com/google/go-github/github/activity_watching.go index 150cf66..c002b3b 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/activity_watching.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/activity_watching.go @@ -50,13 +50,18 @@ func (s *ActivityService) ListWatchers(owner, repo string, opt *ListOptions) ([] // the empty string will fetch watched repos for the authenticated user. // // GitHub API Docs: https://developer.github.com/v3/activity/watching/#list-repositories-being-watched -func (s *ActivityService) ListWatched(user string) ([]Repository, *Response, error) { +func (s *ActivityService) ListWatched(user string, opt *ListOptions) ([]Repository, *Response, error) { var u string if user != "" { u = fmt.Sprintf("users/%v/subscriptions", user) } else { u = "user/subscriptions" } + u, err := addOptions(u, opt) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest("GET", u, nil) if err != nil { return nil, nil, err diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/doc.go b/Godeps/_workspace/src/github.com/google/go-github/github/doc.go similarity index 99% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/doc.go rename to Godeps/_workspace/src/github.com/google/go-github/github/doc.go index b4ac8e6..e2066f1 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/doc.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/doc.go @@ -74,7 +74,7 @@ The GitHub API has good support for conditional requests which will help prevent you from burning through your rate limit, as well as help speed up your application. go-github does not handle conditional requests directly, but is instead designed to work with a caching http.Transport. We recommend using -https://github.com/gregjones/httpcache, which can be used in conjuction with +https://github.com/gregjones/httpcache, which can be used in conjunction with https://github.com/sourcegraph/apiproxy to provide additional flexibility and control of caching rules. diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/gists.go b/Godeps/_workspace/src/github.com/google/go-github/github/gists.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/gists.go rename to Godeps/_workspace/src/github.com/google/go-github/github/gists.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/gists_comments.go b/Godeps/_workspace/src/github.com/google/go-github/github/gists_comments.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/gists_comments.go rename to Godeps/_workspace/src/github.com/google/go-github/github/gists_comments.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/git.go b/Godeps/_workspace/src/github.com/google/go-github/github/git.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/git.go rename to Godeps/_workspace/src/github.com/google/go-github/github/git.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/git_blobs.go b/Godeps/_workspace/src/github.com/google/go-github/github/git_blobs.go similarity index 94% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/git_blobs.go rename to Godeps/_workspace/src/github.com/google/go-github/github/git_blobs.go index 133780b..55148fd 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/git_blobs.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/git_blobs.go @@ -33,7 +33,7 @@ func (s *GitService) GetBlob(owner string, repo string, sha string) (*Blob, *Res // CreateBlob creates a blob object. // -// GitHub API docs: http://developer.github.com/v3/git/blobs/#create-a-blob +// GitHub API docs: https://developer.github.com/v3/git/blobs/#create-a-blob func (s *GitService) CreateBlob(owner string, repo string, blob *Blob) (*Blob, *Response, error) { u := fmt.Sprintf("repos/%v/%v/git/blobs", owner, repo) req, err := s.client.NewRequest("POST", u, blob) diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/git_commits.go b/Godeps/_workspace/src/github.com/google/go-github/github/git_commits.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/git_commits.go rename to Godeps/_workspace/src/github.com/google/go-github/github/git_commits.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/git_refs.go b/Godeps/_workspace/src/github.com/google/go-github/github/git_refs.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/git_refs.go rename to Godeps/_workspace/src/github.com/google/go-github/github/git_refs.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/git_tags.go b/Godeps/_workspace/src/github.com/google/go-github/github/git_tags.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/git_tags.go rename to Godeps/_workspace/src/github.com/google/go-github/github/git_tags.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/git_trees.go b/Godeps/_workspace/src/github.com/google/go-github/github/git_trees.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/git_trees.go rename to Godeps/_workspace/src/github.com/google/go-github/github/git_trees.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/github.go b/Godeps/_workspace/src/github.com/google/go-github/github/github.go similarity index 86% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/github.go rename to Godeps/_workspace/src/github.com/google/go-github/github/github.go index 737c17c..344cbff 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/github.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/github.go @@ -17,6 +17,7 @@ import ( "reflect" "strconv" "strings" + "sync" "time" "github.com/google/go-querystring/query" @@ -31,6 +32,7 @@ const ( headerRateLimit = "X-RateLimit-Limit" headerRateRemaining = "X-RateLimit-Remaining" headerRateReset = "X-RateLimit-Reset" + headerOTP = "X-GitHub-OTP" mediaTypeV3 = "application/vnd.github.v3+json" defaultMediaType = "application/octet-stream" @@ -46,6 +48,9 @@ const ( // https://developer.github.com/changes/2015-06-24-api-enhancements-for-working-with-organization-permissions/ mediaTypeOrgPermissionPreview = "application/vnd.github.ironman-preview+json" mediaTypeOrgPermissionRepoPreview = "application/vnd.github.ironman-preview.repository+json" + + // https://developer.github.com/changes/2015-11-11-protected-branches-api/ + mediaTypeProtectedBranchesPreview = "application/vnd.github.loki-preview+json" ) // A Client manages communication with the GitHub API. @@ -64,11 +69,8 @@ type Client struct { // User agent used when communicating with the GitHub API. UserAgent string - // Rate specifies the current rate limit for the client as determined by the - // most recent API call. If the client is used in a multi-user application, - // this rate may not always be up-to-date. Call RateLimit() to check the - // current rate. - Rate Rate + rateMu sync.Mutex + rate Rate // Services used for talking to different parts of the GitHub API. Activity *ActivityService @@ -292,6 +294,17 @@ func (r *Response) populateRate() { } } +// Rate specifies the current rate limit for the client as determined by the +// most recent API call. If the client is used in a multi-user application, +// this rate may not always be up-to-date. Call RateLimits() to check the +// current rate. +func (c *Client) Rate() Rate { + c.rateMu.Lock() + rate := c.rate + c.rateMu.Unlock() + return rate +} + // Do sends an API request and returns the API response. The API response is // JSON decoded and stored in the value pointed to by v, or returned as an // error if an API error has occurred. If v implements the io.Writer @@ -307,7 +320,9 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) { response := newResponse(resp) - c.Rate = response.Rate + c.rateMu.Lock() + c.rate = response.Rate + c.rateMu.Unlock() err = CheckResponse(resp) if err != nil { @@ -321,6 +336,9 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) { io.Copy(w, resp.Body) } else { err = json.NewDecoder(resp.Body).Decode(v) + if err == io.EOF { + err = nil // ignore EOF errors caused by empty response body + } } } return response, err @@ -343,8 +361,15 @@ func (r *ErrorResponse) Error() string { r.Response.StatusCode, r.Message, r.Errors) } -// sanitizeURL redacts the client_id and client_secret tokens from the URL which -// may be exposed to the user, specifically in the ErrorResponse error message. +// TwoFactorAuthError occurs when using HTTP Basic Authentication for a user +// that has two-factor authentication enabled. The request can be reattempted +// by providing a one-time password in the request. +type TwoFactorAuthError ErrorResponse + +func (r *TwoFactorAuthError) Error() string { return (*ErrorResponse)(r).Error() } + +// sanitizeURL redacts the client_secret parameter from the URL which may be +// exposed to the user, specifically in the ErrorResponse error message. func sanitizeURL(uri *url.URL) *url.URL { if uri == nil { return nil @@ -397,6 +422,9 @@ func CheckResponse(r *http.Response) error { if err == nil && data != nil { json.Unmarshal(data, errorResponse) } + if r.StatusCode == http.StatusUnauthorized && strings.HasPrefix(r.Header.Get(headerOTP), "required") { + return (*TwoFactorAuthError)(errorResponse) + } return errorResponse } @@ -548,6 +576,43 @@ func (t *UnauthenticatedRateLimitedTransport) transport() http.RoundTripper { return http.DefaultTransport } +// BasicAuthTransport is an http.RoundTripper that authenticates all requests +// using HTTP Basic Authentication with the provided username and password. It +// additionally supports users who have two-factor authentication enabled on +// their GitHub account. +type BasicAuthTransport struct { + Username string // GitHub username + Password string // GitHub password + OTP string // one-time password for users with two-factor auth enabled + + // Transport is the underlying HTTP transport to use when making requests. + // It will default to http.DefaultTransport if nil. + Transport http.RoundTripper +} + +// RoundTrip implements the RoundTripper interface. +func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) { + req = cloneRequest(req) // per RoundTrip contract + req.SetBasicAuth(t.Username, t.Password) + if t.OTP != "" { + req.Header.Add(headerOTP, t.OTP) + } + return t.transport().RoundTrip(req) +} + +// Client returns an *http.Client that makes requests that are authenticated +// using HTTP Basic Authentication. +func (t *BasicAuthTransport) Client() *http.Client { + return &http.Client{Transport: t} +} + +func (t *BasicAuthTransport) transport() http.RoundTripper { + if t.Transport != nil { + return t.Transport + } + return http.DefaultTransport +} + // cloneRequest returns a clone of the provided *http.Request. The clone is a // shallow copy of the struct and its Header map. func cloneRequest(r *http.Request) *http.Request { @@ -555,9 +620,9 @@ func cloneRequest(r *http.Request) *http.Request { r2 := new(http.Request) *r2 = *r // deep copy of the Header - r2.Header = make(http.Header) + r2.Header = make(http.Header, len(r.Header)) for k, s := range r.Header { - r2.Header[k] = s + r2.Header[k] = append([]string(nil), s...) } return r2 } diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/gitignore.go b/Godeps/_workspace/src/github.com/google/go-github/github/gitignore.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/gitignore.go rename to Godeps/_workspace/src/github.com/google/go-github/github/gitignore.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues.go b/Godeps/_workspace/src/github.com/google/go-github/github/issues.go similarity index 97% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues.go rename to Godeps/_workspace/src/github.com/google/go-github/github/issues.go index 05f5477..a60cede 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/issues.go @@ -65,7 +65,7 @@ type IssueListOptions struct { Filter string `url:"filter,omitempty"` // State filters issues based on their state. Possible values are: open, - // closed. Default is "open". + // closed, all. Default is "open". State string `url:"state,omitempty"` // Labels filters issues based on their label. @@ -76,7 +76,7 @@ type IssueListOptions struct { Sort string `url:"sort,omitempty"` // Direction in which to sort issues. Possible values are: asc, desc. - // Default is "asc". + // Default is "desc". Direction string `url:"direction,omitempty"` // Since filters issues by time. @@ -148,7 +148,7 @@ type IssueListByRepoOptions struct { Milestone string `url:"milestone,omitempty"` // State filters issues based on their state. Possible values are: open, - // closed. Default is "open". + // closed, all. Default is "open". State string `url:"state,omitempty"` // Assignee filters issues based on their assignee. Possible values are a @@ -156,10 +156,10 @@ type IssueListByRepoOptions struct { // any assigned user. Assignee string `url:"assignee,omitempty"` - // Assignee filters issues based on their creator. + // Creator filters issues based on their creator. Creator string `url:"creator,omitempty"` - // Assignee filters issues to those mentioned a specific user. + // Mentioned filters issues to those mentioned a specific user. Mentioned string `url:"mentioned,omitempty"` // Labels filters issues based on their label. @@ -170,7 +170,7 @@ type IssueListByRepoOptions struct { Sort string `url:"sort,omitempty"` // Direction in which to sort issues. Possible values are: asc, desc. - // Default is "asc". + // Default is "desc". Direction string `url:"direction,omitempty"` // Since filters issues by time. diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues_assignees.go b/Godeps/_workspace/src/github.com/google/go-github/github/issues_assignees.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues_assignees.go rename to Godeps/_workspace/src/github.com/google/go-github/github/issues_assignees.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues_comments.go b/Godeps/_workspace/src/github.com/google/go-github/github/issues_comments.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues_comments.go rename to Godeps/_workspace/src/github.com/google/go-github/github/issues_comments.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues_events.go b/Godeps/_workspace/src/github.com/google/go-github/github/issues_events.go similarity index 60% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues_events.go rename to Godeps/_workspace/src/github.com/google/go-github/github/issues_events.go index 0c720aa..9062d4d 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues_events.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/issues_events.go @@ -22,40 +22,52 @@ type IssueEvent struct { // values are: // // closed - // The issue was closed by the actor. When the commit_id is - // present, it identifies the commit that closed the issue using - // “closes / fixes #NN” syntax. - // - // reopened - // The issue was reopened by the actor. - // - // subscribed - // The actor subscribed to receive notifications for an issue. + // The Actor closed the issue. + // If the issue was closed by commit message, CommitID holds the SHA1 hash of the commit. // // merged - // The issue was merged by the actor. The commit_id attribute is the SHA1 of the HEAD commit that was merged. + // The Actor merged into master a branch containing a commit mentioning the issue. + // CommitID holds the SHA1 of the merge commit. // // referenced - // The issue was referenced from a commit message. The commit_id attribute is the commit SHA1 of where that happened. + // The Actor committed to master a commit mentioning the issue in its commit message. + // CommitID holds the SHA1 of the commit. + // + // reopened, locked, unlocked + // The Actor did that to the issue. + // + // renamed + // The Actor changed the issue title from Rename.From to Rename.To. // // mentioned - // The actor was @mentioned in an issue body. + // Someone unspecified @mentioned the Actor [sic] in an issue comment body. // - // assigned - // The issue was assigned to the actor. + // assigned, unassigned + // The Actor assigned the issue to or removed the assignment from the Assignee. // - // head_ref_deleted - // The pull request’s branch was deleted. + // labeled, unlabeled + // The Actor added or removed the Label from the issue. + // + // milestoned, demilestoned + // The Actor added or removed the issue from the Milestone. + // + // subscribed, unsubscribed + // The Actor subscribed to or unsubscribed from notifications for an issue. + // + // head_ref_deleted, head_ref_restored + // The pull request’s branch was deleted or restored. // - // head_ref_restored - // The pull request’s branch was restored. Event *string `json:"event,omitempty"` - // The SHA of the commit that referenced this commit, if applicable. - CommitID *string `json:"commit_id,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` Issue *Issue `json:"issue,omitempty"` + + // Only present on certain events; see above. + Assignee *User `json:"assignee,omitempty"` + CommitID *string `json:"commit_id,omitempty"` + Milestone *Milestone `json:"milestone,omitempty"` + Label *Label `json:"label,omitempty"` + Rename *Rename `json:"rename,omitempty"` } // ListIssueEvents lists events for the specified issue. @@ -125,3 +137,13 @@ func (s *IssuesService) GetEvent(owner, repo string, id int) (*IssueEvent, *Resp return event, resp, err } + +// Rename contains details for 'renamed' events. +type Rename struct { + From *string `json:"from,omitempty"` + To *string `json:"to,omitempty"` +} + +func (r Rename) String() string { + return Stringify(r) +} diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues_labels.go b/Godeps/_workspace/src/github.com/google/go-github/github/issues_labels.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues_labels.go rename to Godeps/_workspace/src/github.com/google/go-github/github/issues_labels.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues_milestones.go b/Godeps/_workspace/src/github.com/google/go-github/github/issues_milestones.go similarity index 95% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues_milestones.go rename to Godeps/_workspace/src/github.com/google/go-github/github/issues_milestones.go index d5fd8ae..cbd7920 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/issues_milestones.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/issues_milestones.go @@ -49,7 +49,7 @@ type MilestoneListOptions struct { // // GitHub API docs: https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository func (s *IssuesService) ListMilestones(owner string, repo string, opt *MilestoneListOptions) ([]Milestone, *Response, error) { - u := fmt.Sprintf("/repos/%v/%v/milestones", owner, repo) + u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo) u, err := addOptions(u, opt) if err != nil { return nil, nil, err @@ -73,7 +73,7 @@ func (s *IssuesService) ListMilestones(owner string, repo string, opt *Milestone // // GitHub API docs: https://developer.github.com/v3/issues/milestones/#get-a-single-milestone func (s *IssuesService) GetMilestone(owner string, repo string, number int) (*Milestone, *Response, error) { - u := fmt.Sprintf("/repos/%v/%v/milestones/%d", owner, repo, number) + u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) req, err := s.client.NewRequest("GET", u, nil) if err != nil { return nil, nil, err @@ -92,7 +92,7 @@ func (s *IssuesService) GetMilestone(owner string, repo string, number int) (*Mi // // GitHub API docs: https://developer.github.com/v3/issues/milestones/#create-a-milestone func (s *IssuesService) CreateMilestone(owner string, repo string, milestone *Milestone) (*Milestone, *Response, error) { - u := fmt.Sprintf("/repos/%v/%v/milestones", owner, repo) + u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo) req, err := s.client.NewRequest("POST", u, milestone) if err != nil { return nil, nil, err diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/licenses.go b/Godeps/_workspace/src/github.com/google/go-github/github/licenses.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/licenses.go rename to Godeps/_workspace/src/github.com/google/go-github/github/licenses.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/misc.go b/Godeps/_workspace/src/github.com/google/go-github/github/misc.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/misc.go rename to Godeps/_workspace/src/github.com/google/go-github/github/misc.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/orgs.go b/Godeps/_workspace/src/github.com/google/go-github/github/orgs.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/orgs.go rename to Godeps/_workspace/src/github.com/google/go-github/github/orgs.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/orgs_hooks.go b/Godeps/_workspace/src/github.com/google/go-github/github/orgs_hooks.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/orgs_hooks.go rename to Godeps/_workspace/src/github.com/google/go-github/github/orgs_hooks.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/orgs_members.go b/Godeps/_workspace/src/github.com/google/go-github/github/orgs_members.go similarity index 97% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/orgs_members.go rename to Godeps/_workspace/src/github.com/google/go-github/github/orgs_members.go index 554cb1d..01a9ba9 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/orgs_members.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/orgs_members.go @@ -52,7 +52,7 @@ type ListMembersOptions struct { // 2fa_disabled, all. Default is "all". Filter string `url:"filter,omitempty"` - // Role filters memebers returned by their role in the organization. + // Role filters members returned by their role in the organization. // Possible values are: // all - all members of the organization, regardless of role // admin - organization owners @@ -172,7 +172,7 @@ func (s *OrganizationsService) ConcealMembership(org, user string) (*Response, e // ListOrgMembershipsOptions specifies optional parameters to the // OrganizationsService.ListOrgMemberships method. type ListOrgMembershipsOptions struct { - // Filter memberships to include only those withe the specified state. + // Filter memberships to include only those with the specified state. // Possible values are: "active", "pending". State string `url:"state,omitempty"` @@ -238,14 +238,16 @@ func (s *OrganizationsService) GetOrgMembership(user, org string) (*Membership, // GitHub API docs: https://developer.github.com/v3/orgs/members/#add-or-update-organization-membership // GitHub API docs: https://developer.github.com/v3/orgs/members/#edit-your-organization-membership func (s *OrganizationsService) EditOrgMembership(user, org string, membership *Membership) (*Membership, *Response, error) { - var u string + var u, method string if user != "" { u = fmt.Sprintf("orgs/%v/memberships/%v", org, user) + method = "PUT" } else { u = fmt.Sprintf("user/memberships/orgs/%v", org) + method = "PATCH" } - req, err := s.client.NewRequest("PATCH", u, membership) + req, err := s.client.NewRequest(method, u, membership) if err != nil { return nil, nil, err } diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/orgs_teams.go b/Godeps/_workspace/src/github.com/google/go-github/github/orgs_teams.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/orgs_teams.go rename to Godeps/_workspace/src/github.com/google/go-github/github/orgs_teams.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/pulls.go b/Godeps/_workspace/src/github.com/google/go-github/github/pulls.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/pulls.go rename to Godeps/_workspace/src/github.com/google/go-github/github/pulls.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/pulls_comments.go b/Godeps/_workspace/src/github.com/google/go-github/github/pulls_comments.go similarity index 86% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/pulls_comments.go rename to Godeps/_workspace/src/github.com/google/go-github/github/pulls_comments.go index 35c7241..f165d5f 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/pulls_comments.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/pulls_comments.go @@ -12,14 +12,17 @@ import ( // PullRequestComment represents a comment left on a pull request. type PullRequestComment struct { - ID *int `json:"id,omitempty"` - Body *string `json:"body,omitempty"` - Path *string `json:"path,omitempty"` - Position *int `json:"position,omitempty"` - CommitID *string `json:"commit_id,omitempty"` - User *User `json:"user,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` + ID *int `json:"id,omitempty"` + Body *string `json:"body,omitempty"` + Path *string `json:"path,omitempty"` + DiffHunk *string `json:"diff_hunk,omitempty"` + Position *int `json:"position,omitempty"` + OriginalPosition *int `json:"original_position,omitempty"` + CommitID *string `json:"commit_id,omitempty"` + OriginalCommitID *string `json:"original_commit_id,omitempty"` + User *User `json:"user,omitempty"` + CreatedAt *time.Time `json:"created_at,omitempty"` + UpdatedAt *time.Time `json:"updated_at,omitempty"` } func (p PullRequestComment) String() string { diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos.go similarity index 90% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos.go index ed9052c..0f8353f 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/repos.go @@ -146,6 +146,9 @@ func (s *RepositoriesService) List(user string, opt *RepositoryListOptions) ([]R return nil, nil, err } + // TODO: remove custom Accept header when license support fully launches + req.Header.Set("Accept", mediaTypeLicensesPreview) + repos := new([]Repository) resp, err := s.client.Do(req, repos) if err != nil { @@ -180,6 +183,9 @@ func (s *RepositoriesService) ListByOrg(org string, opt *RepositoryListByOrgOpti return nil, nil, err } + // TODO: remove custom Accept header when license support fully launches + req.Header.Set("Accept", mediaTypeLicensesPreview) + repos := new([]Repository) resp, err := s.client.Do(req, repos) if err != nil { @@ -442,8 +448,27 @@ func (s *RepositoriesService) ListTags(owner string, repo string, opt *ListOptio // Branch represents a repository branch type Branch struct { - Name *string `json:"name,omitempty"` - Commit *Commit `json:"commit,omitempty"` + Name *string `json:"name,omitempty"` + Commit *Commit `json:"commit,omitempty"` + Protection *Protection `json:"protection,omitempty"` +} + +// Protection represents a repository branch's protection +type Protection struct { + Enabled *bool `json:"enabled,omitempty"` + RequiredStatusChecks *RequiredStatusChecks `json:"required_status_checks,omitempty"` +} + +// RequiredStatusChecks represents the protection status of a individual branch +type RequiredStatusChecks struct { + // Who required status checks apply to. + // Possible values are: + // off + // non_admins + // everyone + EnforcementLevel *string `json:"enforcement_level,omitempty"` + // The list of status checks which are required + Contexts *[]string `json:"contexts,omitempty"` } // ListBranches lists branches for the specified repository. @@ -480,6 +505,29 @@ func (s *RepositoriesService) GetBranch(owner, repo, branch string) (*Branch, *R return nil, nil, err } + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + + b := new(Branch) + resp, err := s.client.Do(req, b) + if err != nil { + return nil, resp, err + } + + return b, resp, err +} + +// EditBranch edits the branch (currently only Branch Protection) +// +// GitHub API docs: https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection +func (s *RepositoriesService) EditBranch(owner, repo, branchName string, branch *Branch) (*Branch, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/branches/%v", owner, repo, branchName) + req, err := s.client.NewRequest("PATCH", u, branch) + if err != nil { + return nil, nil, err + } + + req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) + b := new(Branch) resp, err := s.client.Do(req, b) if err != nil { diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_collaborators.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos_collaborators.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_collaborators.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos_collaborators.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_comments.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos_comments.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_comments.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos_comments.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_commits.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos_commits.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_commits.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos_commits.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_contents.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos_contents.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_contents.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos_contents.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_deployments.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos_deployments.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_deployments.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos_deployments.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_forks.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos_forks.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_forks.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos_forks.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_hooks.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos_hooks.go similarity index 99% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_hooks.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos_hooks.go index bc4c8c5..36734ff 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_hooks.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/repos_hooks.go @@ -70,6 +70,7 @@ type Hook struct { CreatedAt *time.Time `json:"created_at,omitempty"` UpdatedAt *time.Time `json:"updated_at,omitempty"` Name *string `json:"name,omitempty"` + URL *string `json:"url,omitempty"` Events []string `json:"events,omitempty"` Active *bool `json:"active,omitempty"` Config map[string]interface{} `json:"config,omitempty"` diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_keys.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos_keys.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_keys.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos_keys.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_merging.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos_merging.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_merging.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos_merging.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_pages.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos_pages.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_pages.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos_pages.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_releases.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos_releases.go similarity index 92% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_releases.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos_releases.go index f685a28..4e4e2ab 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_releases.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/repos_releases.go @@ -10,7 +10,6 @@ import ( "fmt" "io" "mime" - "net/http" "os" "path/filepath" ) @@ -213,9 +212,9 @@ func (s *RepositoriesService) GetReleaseAsset(owner, repo string, id int) (*Rele return asset, resp, err } -// DowloadReleaseAsset downloads a release asset. +// DownloadReleaseAsset downloads a release asset. // -// DowloadReleaseAsset returns an io.ReadCloser that reads the contents of the +// DownloadReleaseAsset returns an io.ReadCloser that reads the contents of the // specified release asset. It is the caller's responsibility to close the ReadCloser. // // GitHub API docs : http://developer.github.com/v3/repos/releases/#get-a-single-release-asset @@ -228,32 +227,7 @@ func (s *RepositoriesService) DownloadReleaseAsset(owner, repo string, id int) ( } req.Header.Set("Accept", defaultMediaType) - var resp *http.Response - if s.client.client.Transport == nil { - resp, err = http.DefaultTransport.RoundTrip(req) - } else { - resp, err = s.client.client.Transport.RoundTrip(req) - } - if err != nil { - return nil, err - } - - // GitHub API streamed the asset directly - if resp.StatusCode == http.StatusOK { - return resp.Body, nil - } - - if resp.StatusCode != http.StatusFound { - return nil, fmt.Errorf("Expected status code 200 or 302, got %d", resp.StatusCode) - } - - // GitHub API redirected to pre-signed S3 URL - downloadURL, err := resp.Location() - if err != nil { - return nil, err - } - - resp, err = http.Get(downloadURL.String()) + resp, err := s.client.client.Do(req) if err != nil { return nil, err } diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_stats.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos_stats.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_stats.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos_stats.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_statuses.go b/Godeps/_workspace/src/github.com/google/go-github/github/repos_statuses.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/repos_statuses.go rename to Godeps/_workspace/src/github.com/google/go-github/github/repos_statuses.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/search.go b/Godeps/_workspace/src/github.com/google/go-github/github/search.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/search.go rename to Godeps/_workspace/src/github.com/google/go-github/github/search.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/strings.go b/Godeps/_workspace/src/github.com/google/go-github/github/strings.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/strings.go rename to Godeps/_workspace/src/github.com/google/go-github/github/strings.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/timestamp.go b/Godeps/_workspace/src/github.com/google/go-github/github/timestamp.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/timestamp.go rename to Godeps/_workspace/src/github.com/google/go-github/github/timestamp.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/users.go b/Godeps/_workspace/src/github.com/google/go-github/github/users.go similarity index 99% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/users.go rename to Godeps/_workspace/src/github.com/google/go-github/github/users.go index 95cca6b..a041bbf 100644 --- a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/users.go +++ b/Godeps/_workspace/src/github.com/google/go-github/github/users.go @@ -113,7 +113,7 @@ func (s *UsersService) Edit(user *User) (*User, *Response, error) { return uResp, resp, err } -// UserListOptions specifies optional parameters to the UsersService.List +// UserListOptions specifies optional parameters to the UsersService.ListAll // method. type UserListOptions struct { // ID of the last user seen diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/users_administration.go b/Godeps/_workspace/src/github.com/google/go-github/github/users_administration.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/users_administration.go rename to Godeps/_workspace/src/github.com/google/go-github/github/users_administration.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/users_emails.go b/Godeps/_workspace/src/github.com/google/go-github/github/users_emails.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/users_emails.go rename to Godeps/_workspace/src/github.com/google/go-github/github/users_emails.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/users_followers.go b/Godeps/_workspace/src/github.com/google/go-github/github/users_followers.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/users_followers.go rename to Godeps/_workspace/src/github.com/google/go-github/github/users_followers.go diff --git a/Godeps/_workspace/src/github.com/zachgersh/go-github/github/users_keys.go b/Godeps/_workspace/src/github.com/google/go-github/github/users_keys.go similarity index 100% rename from Godeps/_workspace/src/github.com/zachgersh/go-github/github/users_keys.go rename to Godeps/_workspace/src/github.com/google/go-github/github/users_keys.go diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/config/config.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/config/config.go index 46ce16a..7b22e34 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/config/config.go +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/config/config.go @@ -79,7 +79,7 @@ func Flags(flagSet *flag.FlagSet, prefix string, includeParallelFlags bool) { } flagSet.BoolVar(&(DefaultReporterConfig.NoColor), prefix+"noColor", false, "If set, suppress color output in default reporter.") - flagSet.Float64Var(&(DefaultReporterConfig.SlowSpecThreshold), prefix+"slowSpecThreshold", 5.0, "(in seconds) Specs that take longer to run than this threshold are flagged as slow by the default reporter (default: 5 seconds).") + flagSet.Float64Var(&(DefaultReporterConfig.SlowSpecThreshold), prefix+"slowSpecThreshold", 5.0, "(in seconds) Specs that take longer to run than this threshold are flagged as slow by the default reporter.") flagSet.BoolVar(&(DefaultReporterConfig.NoisyPendings), prefix+"noisyPendings", true, "If set, default reporter will shout about pending tests.") flagSet.BoolVar(&(DefaultReporterConfig.Verbose), prefix+"v", false, "If set, default reporter print out all specs as they begin.") flagSet.BoolVar(&(DefaultReporterConfig.Succinct), prefix+"succinct", false, "If set, default reporter prints out a very succinct report") diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/nodot_command.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/nodot_command.go index e1a2e13..212235b 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/nodot_command.go +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/nodot_command.go @@ -63,6 +63,8 @@ func findSuiteFile() (string, os.FileMode) { if err != nil { complainAndQuit("Could not find suite file for nodot: " + err.Error()) } + defer f.Close() + if re.MatchReader(bufio.NewReader(f)) { return path, file.Mode() } diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go index cc7d2f4..4ef3bc4 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go @@ -4,6 +4,7 @@ import ( "errors" "io/ioutil" "os" + "path" "path/filepath" "regexp" "strings" @@ -47,6 +48,15 @@ func PrecompiledTestSuite(path string) (TestSuite, error) { func SuitesInDir(dir string, recurse bool) []TestSuite { suites := []TestSuite{} + + // "This change will only be enabled if the go command is run with + // GO15VENDOREXPERIMENT=1 in its environment." + // c.f. the vendor-experiment proposal https://goo.gl/2ucMeC + vendorExperiment := os.Getenv("GO15VENDOREXPERIMENT") + if (vendorExperiment == "1") && path.Base(dir) == "vendor" { + return suites + } + files, _ := ioutil.ReadDir(dir) re := regexp.MustCompile(`_test\.go$`) for _, file := range files { diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/unfocus_command.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/unfocus_command.go index 16f3c3b..683c3a9 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/unfocus_command.go +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/ginkgo/unfocus_command.go @@ -24,6 +24,8 @@ func unfocusSpecs([]string, []string) { unfocus("Context") unfocus("It") unfocus("Measure") + unfocus("DescribeTable") + unfocus("Entry") } func unfocus(component string) { diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/internal/leafnodes/runner.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/internal/leafnodes/runner.go index 003f851..870ad82 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/internal/leafnodes/runner.go +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/internal/leafnodes/runner.go @@ -68,7 +68,7 @@ func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure) done := make(chan interface{}, 1) go func() { - finished := false + finished := false defer func() { if e := recover(); e != nil || !finished { @@ -83,7 +83,7 @@ func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure) }() r.asyncFunc(done) - finished = true + finished = true }() select { @@ -96,7 +96,7 @@ func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure) return } func (r *runner) runSync() (outcome types.SpecState, failure types.SpecFailure) { - finished := false + finished := false defer func() { if e := recover(); e != nil || !finished { @@ -107,7 +107,7 @@ func (r *runner) runSync() (outcome types.SpecState, failure types.SpecFailure) }() r.syncFunc() - finished = true + finished = true return } diff --git a/Godeps/_workspace/src/github.com/onsi/ginkgo/reporters/stenographer/stenographer.go b/Godeps/_workspace/src/github.com/onsi/ginkgo/reporters/stenographer/stenographer.go index eeb5cbb..5b5d905 100644 --- a/Godeps/_workspace/src/github.com/onsi/ginkgo/reporters/stenographer/stenographer.go +++ b/Godeps/_workspace/src/github.com/onsi/ginkgo/reporters/stenographer/stenographer.go @@ -8,6 +8,7 @@ package stenographer import ( "fmt" + "runtime" "strings" "github.com/onsi/ginkgo/types" @@ -59,14 +60,20 @@ type Stenographer interface { } func New(color bool) Stenographer { + denoter := "•" + if runtime.GOOS == "windows" { + denoter = "+" + } return &consoleStenographer{ color: color, + denoter: denoter, cursorState: cursorStateTop, } } type consoleStenographer struct { color bool + denoter string cursorState cursorStateType } @@ -216,13 +223,13 @@ func (s *consoleStenographer) AnnounceCapturedOutput(output string) { } func (s *consoleStenographer) AnnounceSuccesfulSpec(spec *types.SpecSummary) { - s.print(0, s.colorize(greenColor, "•")) + s.print(0, s.colorize(greenColor, s.denoter)) s.stream() } func (s *consoleStenographer) AnnounceSuccesfulSlowSpec(spec *types.SpecSummary, succinct bool) { s.printBlockWithMessage( - s.colorize(greenColor, "• [SLOW TEST:%.3f seconds]", spec.RunTime.Seconds()), + s.colorize(greenColor, "%s [SLOW TEST:%.3f seconds]", s.denoter, spec.RunTime.Seconds()), "", spec, succinct, @@ -231,7 +238,7 @@ func (s *consoleStenographer) AnnounceSuccesfulSlowSpec(spec *types.SpecSummary, func (s *consoleStenographer) AnnounceSuccesfulMeasurement(spec *types.SpecSummary, succinct bool) { s.printBlockWithMessage( - s.colorize(greenColor, "• [MEASUREMENT]"), + s.colorize(greenColor, "%s [MEASUREMENT]", s.denoter), s.measurementReport(spec, succinct), spec, succinct, @@ -270,15 +277,15 @@ func (s *consoleStenographer) AnnounceSkippedSpec(spec *types.SpecSummary, succi } func (s *consoleStenographer) AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool) { - s.printSpecFailure("•... Timeout", spec, succinct, fullTrace) + s.printSpecFailure(fmt.Sprintf("%s... Timeout", s.denoter), spec, succinct, fullTrace) } func (s *consoleStenographer) AnnounceSpecPanicked(spec *types.SpecSummary, succinct bool, fullTrace bool) { - s.printSpecFailure("•! Panic", spec, succinct, fullTrace) + s.printSpecFailure(fmt.Sprintf("%s! Panic", s.denoter), spec, succinct, fullTrace) } func (s *consoleStenographer) AnnounceSpecFailed(spec *types.SpecSummary, succinct bool, fullTrace bool) { - s.printSpecFailure("• Failure", spec, succinct, fullTrace) + s.printSpecFailure(fmt.Sprintf("%s Failure", s.denoter), spec, succinct, fullTrace) } func (s *consoleStenographer) SummarizeFailures(summaries []*types.SpecSummary) { diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/.gitignore b/Godeps/_workspace/src/github.com/onsi/gomega/.gitignore index 5514532..720c13c 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/.gitignore +++ b/Godeps/_workspace/src/github.com/onsi/gomega/.gitignore @@ -1,3 +1,5 @@ .DS_Store *.test . +.idea +gomega.iml diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_occurred_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_occurred_matcher.go index cdc1d54..ebdd717 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_occurred_matcher.go +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_occurred_matcher.go @@ -10,15 +10,18 @@ type HaveOccurredMatcher struct { } func (matcher *HaveOccurredMatcher) Match(actual interface{}) (success bool, err error) { - if isNil(actual) { + // is purely nil? + if actual == nil { return false, nil } - if isError(actual) { - return true, nil + // must be an 'error' type + if !isError(actual) { + return false, fmt.Errorf("Expected an error-type. Got:\n%s", format.Object(actual, 1)) } - return false, fmt.Errorf("Expected an error. Got:\n%s", format.Object(actual, 1)) + // must be non-nil (or a pointer to a non-nil) + return !isNil(actual), nil } func (matcher *HaveOccurredMatcher) FailureMessage(actual interface{}) (message string) { diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_suffix_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_suffix_matcher.go index eb1b284..afc78fc 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_suffix_matcher.go +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/have_suffix_matcher.go @@ -16,7 +16,7 @@ func (matcher *HaveSuffixMatcher) Match(actual interface{}) (success bool, err e return false, fmt.Errorf("HaveSuffix matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1)) } suffix := matcher.suffix() - return len(actualString) >= len(suffix) && actualString[len(actualString) - len(suffix):] == suffix, nil + return len(actualString) >= len(suffix) && actualString[len(actualString)-len(suffix):] == suffix, nil } func (matcher *HaveSuffixMatcher) suffix() string { diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/succeed_matcher.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/succeed_matcher.go index f7dd853..721ed55 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/succeed_matcher.go +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/succeed_matcher.go @@ -10,15 +10,18 @@ type SucceedMatcher struct { } func (matcher *SucceedMatcher) Match(actual interface{}) (success bool, err error) { + // is purely nil? if actual == nil { return true, nil } - if isError(actual) { - return false, nil + // must be an 'error' type + if !isError(actual) { + return false, fmt.Errorf("Expected an error-type. Got:\n%s", format.Object(actual, 1)) } - return false, fmt.Errorf("Expected an error-type. Got:\n%s", format.Object(actual, 1)) + // must be nil (or a pointer to a nil) + return isNil(actual), nil } func (matcher *SucceedMatcher) FailureMessage(actual interface{}) (message string) { diff --git a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/util/util.go b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/util/util.go index a24cd27..d76a1ee 100644 --- a/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/util/util.go +++ b/Godeps/_workspace/src/github.com/onsi/gomega/matchers/support/goraph/util/util.go @@ -3,5 +3,5 @@ package util import "math" func Odd(n int) bool { - return math.Mod(float64(n), 2.0) == 1.0 -} + return math.Mod(float64(n), 2.0) == 1.0 +} diff --git a/Godeps/_workspace/src/code.google.com/p/goauth2/LICENSE b/Godeps/_workspace/src/golang.org/x/net/LICENSE similarity index 95% rename from Godeps/_workspace/src/code.google.com/p/goauth2/LICENSE rename to Godeps/_workspace/src/golang.org/x/net/LICENSE index 6765f09..6a66aea 100644 --- a/Godeps/_workspace/src/code.google.com/p/goauth2/LICENSE +++ b/Godeps/_workspace/src/golang.org/x/net/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The goauth2 Authors. All rights reserved. +Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/Godeps/_workspace/src/code.google.com/p/goauth2/PATENTS b/Godeps/_workspace/src/golang.org/x/net/PATENTS similarity index 97% rename from Godeps/_workspace/src/code.google.com/p/goauth2/PATENTS rename to Godeps/_workspace/src/golang.org/x/net/PATENTS index 9e87163..7330990 100644 --- a/Godeps/_workspace/src/code.google.com/p/goauth2/PATENTS +++ b/Godeps/_workspace/src/golang.org/x/net/PATENTS @@ -1,7 +1,7 @@ Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by -Google as part of the goauth2 project. +Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) diff --git a/Godeps/_workspace/src/golang.org/x/net/context/context.go b/Godeps/_workspace/src/golang.org/x/net/context/context.go new file mode 100644 index 0000000..11bd8d3 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/context/context.go @@ -0,0 +1,447 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package context defines the Context type, which carries deadlines, +// cancelation signals, and other request-scoped values across API boundaries +// and between processes. +// +// Incoming requests to a server should create a Context, and outgoing calls to +// servers should accept a Context. The chain of function calls between must +// propagate the Context, optionally replacing it with a modified copy created +// using WithDeadline, WithTimeout, WithCancel, or WithValue. +// +// Programs that use Contexts should follow these rules to keep interfaces +// consistent across packages and enable static analysis tools to check context +// propagation: +// +// Do not store Contexts inside a struct type; instead, pass a Context +// explicitly to each function that needs it. The Context should be the first +// parameter, typically named ctx: +// +// func DoSomething(ctx context.Context, arg Arg) error { +// // ... use ctx ... +// } +// +// Do not pass a nil Context, even if a function permits it. Pass context.TODO +// if you are unsure about which Context to use. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +// +// The same Context may be passed to functions running in different goroutines; +// Contexts are safe for simultaneous use by multiple goroutines. +// +// See http://blog.golang.org/context for example code for a server that uses +// Contexts. +package context + +import ( + "errors" + "fmt" + "sync" + "time" +) + +// A Context carries a deadline, a cancelation signal, and other values across +// API boundaries. +// +// Context's methods may be called by multiple goroutines simultaneously. +type Context interface { + // Deadline returns the time when work done on behalf of this context + // should be canceled. Deadline returns ok==false when no deadline is + // set. Successive calls to Deadline return the same results. + Deadline() (deadline time.Time, ok bool) + + // Done returns a channel that's closed when work done on behalf of this + // context should be canceled. Done may return nil if this context can + // never be canceled. Successive calls to Done return the same value. + // + // WithCancel arranges for Done to be closed when cancel is called; + // WithDeadline arranges for Done to be closed when the deadline + // expires; WithTimeout arranges for Done to be closed when the timeout + // elapses. + // + // Done is provided for use in select statements: + // + // // Stream generates values with DoSomething and sends them to out + // // until DoSomething returns an error or ctx.Done is closed. + // func Stream(ctx context.Context, out <-chan Value) error { + // for { + // v, err := DoSomething(ctx) + // if err != nil { + // return err + // } + // select { + // case <-ctx.Done(): + // return ctx.Err() + // case out <- v: + // } + // } + // } + // + // See http://blog.golang.org/pipelines for more examples of how to use + // a Done channel for cancelation. + Done() <-chan struct{} + + // Err returns a non-nil error value after Done is closed. Err returns + // Canceled if the context was canceled or DeadlineExceeded if the + // context's deadline passed. No other values for Err are defined. + // After Done is closed, successive calls to Err return the same value. + Err() error + + // Value returns the value associated with this context for key, or nil + // if no value is associated with key. Successive calls to Value with + // the same key returns the same result. + // + // Use context values only for request-scoped data that transits + // processes and API boundaries, not for passing optional parameters to + // functions. + // + // A key identifies a specific value in a Context. Functions that wish + // to store values in Context typically allocate a key in a global + // variable then use that key as the argument to context.WithValue and + // Context.Value. A key can be any type that supports equality; + // packages should define keys as an unexported type to avoid + // collisions. + // + // Packages that define a Context key should provide type-safe accessors + // for the values stores using that key: + // + // // Package user defines a User type that's stored in Contexts. + // package user + // + // import "golang.org/x/net/context" + // + // // User is the type of value stored in the Contexts. + // type User struct {...} + // + // // key is an unexported type for keys defined in this package. + // // This prevents collisions with keys defined in other packages. + // type key int + // + // // userKey is the key for user.User values in Contexts. It is + // // unexported; clients use user.NewContext and user.FromContext + // // instead of using this key directly. + // var userKey key = 0 + // + // // NewContext returns a new Context that carries value u. + // func NewContext(ctx context.Context, u *User) context.Context { + // return context.WithValue(ctx, userKey, u) + // } + // + // // FromContext returns the User value stored in ctx, if any. + // func FromContext(ctx context.Context) (*User, bool) { + // u, ok := ctx.Value(userKey).(*User) + // return u, ok + // } + Value(key interface{}) interface{} +} + +// Canceled is the error returned by Context.Err when the context is canceled. +var Canceled = errors.New("context canceled") + +// DeadlineExceeded is the error returned by Context.Err when the context's +// deadline passes. +var DeadlineExceeded = errors.New("context deadline exceeded") + +// An emptyCtx is never canceled, has no values, and has no deadline. It is not +// struct{}, since vars of this type must have distinct addresses. +type emptyCtx int + +func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { + return +} + +func (*emptyCtx) Done() <-chan struct{} { + return nil +} + +func (*emptyCtx) Err() error { + return nil +} + +func (*emptyCtx) Value(key interface{}) interface{} { + return nil +} + +func (e *emptyCtx) String() string { + switch e { + case background: + return "context.Background" + case todo: + return "context.TODO" + } + return "unknown empty Context" +} + +var ( + background = new(emptyCtx) + todo = new(emptyCtx) +) + +// Background returns a non-nil, empty Context. It is never canceled, has no +// values, and has no deadline. It is typically used by the main function, +// initialization, and tests, and as the top-level Context for incoming +// requests. +func Background() Context { + return background +} + +// TODO returns a non-nil, empty Context. Code should use context.TODO when +// it's unclear which Context to use or it is not yet available (because the +// surrounding function has not yet been extended to accept a Context +// parameter). TODO is recognized by static analysis tools that determine +// whether Contexts are propagated correctly in a program. +func TODO() Context { + return todo +} + +// A CancelFunc tells an operation to abandon its work. +// A CancelFunc does not wait for the work to stop. +// After the first call, subsequent calls to a CancelFunc do nothing. +type CancelFunc func() + +// WithCancel returns a copy of parent with a new Done channel. The returned +// context's Done channel is closed when the returned cancel function is called +// or when the parent context's Done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { + c := newCancelCtx(parent) + propagateCancel(parent, &c) + return &c, func() { c.cancel(true, Canceled) } +} + +// newCancelCtx returns an initialized cancelCtx. +func newCancelCtx(parent Context) cancelCtx { + return cancelCtx{ + Context: parent, + done: make(chan struct{}), + } +} + +// propagateCancel arranges for child to be canceled when parent is. +func propagateCancel(parent Context, child canceler) { + if parent.Done() == nil { + return // parent is never canceled + } + if p, ok := parentCancelCtx(parent); ok { + p.mu.Lock() + if p.err != nil { + // parent has already been canceled + child.cancel(false, p.err) + } else { + if p.children == nil { + p.children = make(map[canceler]bool) + } + p.children[child] = true + } + p.mu.Unlock() + } else { + go func() { + select { + case <-parent.Done(): + child.cancel(false, parent.Err()) + case <-child.Done(): + } + }() + } +} + +// parentCancelCtx follows a chain of parent references until it finds a +// *cancelCtx. This function understands how each of the concrete types in this +// package represents its parent. +func parentCancelCtx(parent Context) (*cancelCtx, bool) { + for { + switch c := parent.(type) { + case *cancelCtx: + return c, true + case *timerCtx: + return &c.cancelCtx, true + case *valueCtx: + parent = c.Context + default: + return nil, false + } + } +} + +// removeChild removes a context from its parent. +func removeChild(parent Context, child canceler) { + p, ok := parentCancelCtx(parent) + if !ok { + return + } + p.mu.Lock() + if p.children != nil { + delete(p.children, child) + } + p.mu.Unlock() +} + +// A canceler is a context type that can be canceled directly. The +// implementations are *cancelCtx and *timerCtx. +type canceler interface { + cancel(removeFromParent bool, err error) + Done() <-chan struct{} +} + +// A cancelCtx can be canceled. When canceled, it also cancels any children +// that implement canceler. +type cancelCtx struct { + Context + + done chan struct{} // closed by the first cancel call. + + mu sync.Mutex + children map[canceler]bool // set to nil by the first cancel call + err error // set to non-nil by the first cancel call +} + +func (c *cancelCtx) Done() <-chan struct{} { + return c.done +} + +func (c *cancelCtx) Err() error { + c.mu.Lock() + defer c.mu.Unlock() + return c.err +} + +func (c *cancelCtx) String() string { + return fmt.Sprintf("%v.WithCancel", c.Context) +} + +// cancel closes c.done, cancels each of c's children, and, if +// removeFromParent is true, removes c from its parent's children. +func (c *cancelCtx) cancel(removeFromParent bool, err error) { + if err == nil { + panic("context: internal error: missing cancel error") + } + c.mu.Lock() + if c.err != nil { + c.mu.Unlock() + return // already canceled + } + c.err = err + close(c.done) + for child := range c.children { + // NOTE: acquiring the child's lock while holding parent's lock. + child.cancel(false, err) + } + c.children = nil + c.mu.Unlock() + + if removeFromParent { + removeChild(c.Context, c) + } +} + +// WithDeadline returns a copy of the parent context with the deadline adjusted +// to be no later than d. If the parent's deadline is already earlier than d, +// WithDeadline(parent, d) is semantically equivalent to parent. The returned +// context's Done channel is closed when the deadline expires, when the returned +// cancel function is called, or when the parent context's Done channel is +// closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) { + if cur, ok := parent.Deadline(); ok && cur.Before(deadline) { + // The current deadline is already sooner than the new one. + return WithCancel(parent) + } + c := &timerCtx{ + cancelCtx: newCancelCtx(parent), + deadline: deadline, + } + propagateCancel(parent, c) + d := deadline.Sub(time.Now()) + if d <= 0 { + c.cancel(true, DeadlineExceeded) // deadline has already passed + return c, func() { c.cancel(true, Canceled) } + } + c.mu.Lock() + defer c.mu.Unlock() + if c.err == nil { + c.timer = time.AfterFunc(d, func() { + c.cancel(true, DeadlineExceeded) + }) + } + return c, func() { c.cancel(true, Canceled) } +} + +// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to +// implement Done and Err. It implements cancel by stopping its timer then +// delegating to cancelCtx.cancel. +type timerCtx struct { + cancelCtx + timer *time.Timer // Under cancelCtx.mu. + + deadline time.Time +} + +func (c *timerCtx) Deadline() (deadline time.Time, ok bool) { + return c.deadline, true +} + +func (c *timerCtx) String() string { + return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now())) +} + +func (c *timerCtx) cancel(removeFromParent bool, err error) { + c.cancelCtx.cancel(false, err) + if removeFromParent { + // Remove this timerCtx from its parent cancelCtx's children. + removeChild(c.cancelCtx.Context, c) + } + c.mu.Lock() + if c.timer != nil { + c.timer.Stop() + c.timer = nil + } + c.mu.Unlock() +} + +// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete: +// +// func slowOperationWithTimeout(ctx context.Context) (Result, error) { +// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) +// defer cancel() // releases resources if slowOperation completes before timeout elapses +// return slowOperation(ctx) +// } +func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { + return WithDeadline(parent, time.Now().Add(timeout)) +} + +// WithValue returns a copy of parent in which the value associated with key is +// val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +func WithValue(parent Context, key interface{}, val interface{}) Context { + return &valueCtx{parent, key, val} +} + +// A valueCtx carries a key-value pair. It implements Value for that key and +// delegates all other calls to the embedded Context. +type valueCtx struct { + Context + key, val interface{} +} + +func (c *valueCtx) String() string { + return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val) +} + +func (c *valueCtx) Value(key interface{}) interface{} { + if c.key == key { + return c.val + } + return c.Context.Value(key) +} diff --git a/Godeps/_workspace/src/golang.org/x/net/context/ctxhttp/cancelreq.go b/Godeps/_workspace/src/golang.org/x/net/context/ctxhttp/cancelreq.go new file mode 100644 index 0000000..e3170e3 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/context/ctxhttp/cancelreq.go @@ -0,0 +1,19 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.5 + +package ctxhttp + +import "net/http" + +func canceler(client *http.Client, req *http.Request) func() { + // TODO(djd): Respect any existing value of req.Cancel. + ch := make(chan struct{}) + req.Cancel = ch + + return func() { + close(ch) + } +} diff --git a/Godeps/_workspace/src/golang.org/x/net/context/ctxhttp/cancelreq_go14.go b/Godeps/_workspace/src/golang.org/x/net/context/ctxhttp/cancelreq_go14.go new file mode 100644 index 0000000..56bcbad --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/context/ctxhttp/cancelreq_go14.go @@ -0,0 +1,23 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.5 + +package ctxhttp + +import "net/http" + +type requestCanceler interface { + CancelRequest(*http.Request) +} + +func canceler(client *http.Client, req *http.Request) func() { + rc, ok := client.Transport.(requestCanceler) + if !ok { + return func() {} + } + return func() { + rc.CancelRequest(req) + } +} diff --git a/Godeps/_workspace/src/golang.org/x/net/context/ctxhttp/ctxhttp.go b/Godeps/_workspace/src/golang.org/x/net/context/ctxhttp/ctxhttp.go new file mode 100644 index 0000000..26a5e19 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/net/context/ctxhttp/ctxhttp.go @@ -0,0 +1,140 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package ctxhttp provides helper functions for performing context-aware HTTP requests. +package ctxhttp + +import ( + "io" + "net/http" + "net/url" + "strings" + + "golang.org/x/net/context" +) + +func nop() {} + +var ( + testHookContextDoneBeforeHeaders = nop + testHookDoReturned = nop + testHookDidBodyClose = nop +) + +// Do sends an HTTP request with the provided http.Client and returns an HTTP response. +// If the client is nil, http.DefaultClient is used. +// If the context is canceled or times out, ctx.Err() will be returned. +func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) { + if client == nil { + client = http.DefaultClient + } + + // Request cancelation changed in Go 1.5, see cancelreq.go and cancelreq_go14.go. + cancel := canceler(client, req) + + type responseAndError struct { + resp *http.Response + err error + } + result := make(chan responseAndError, 1) + + go func() { + resp, err := client.Do(req) + testHookDoReturned() + result <- responseAndError{resp, err} + }() + + var resp *http.Response + + select { + case <-ctx.Done(): + testHookContextDoneBeforeHeaders() + cancel() + // Clean up after the goroutine calling client.Do: + go func() { + if r := <-result; r.resp != nil { + testHookDidBodyClose() + r.resp.Body.Close() + } + }() + return nil, ctx.Err() + case r := <-result: + var err error + resp, err = r.resp, r.err + if err != nil { + return resp, err + } + } + + c := make(chan struct{}) + go func() { + select { + case <-ctx.Done(): + cancel() + case <-c: + // The response's Body is closed. + } + }() + resp.Body = ¬ifyingReader{resp.Body, c} + + return resp, nil +} + +// Get issues a GET request via the Do function. +func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + return Do(ctx, client, req) +} + +// Head issues a HEAD request via the Do function. +func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) { + req, err := http.NewRequest("HEAD", url, nil) + if err != nil { + return nil, err + } + return Do(ctx, client, req) +} + +// Post issues a POST request via the Do function. +func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) { + req, err := http.NewRequest("POST", url, body) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", bodyType) + return Do(ctx, client, req) +} + +// PostForm issues a POST request via the Do function. +func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) { + return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) +} + +// notifyingReader is an io.ReadCloser that closes the notify channel after +// Close is called or a Read fails on the underlying ReadCloser. +type notifyingReader struct { + io.ReadCloser + notify chan<- struct{} +} + +func (r *notifyingReader) Read(p []byte) (int, error) { + n, err := r.ReadCloser.Read(p) + if err != nil && r.notify != nil { + close(r.notify) + r.notify = nil + } + return n, err +} + +func (r *notifyingReader) Close() error { + err := r.ReadCloser.Close() + if r.notify != nil { + close(r.notify) + r.notify = nil + } + return err +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/.travis.yml b/Godeps/_workspace/src/golang.org/x/oauth2/.travis.yml new file mode 100644 index 0000000..a035125 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/.travis.yml @@ -0,0 +1,14 @@ +language: go + +go: + - 1.3 + - 1.4 + +install: + - export GOPATH="$HOME/gopath" + - mkdir -p "$GOPATH/src/golang.org/x" + - mv "$TRAVIS_BUILD_DIR" "$GOPATH/src/golang.org/x/oauth2" + - go get -v -t -d golang.org/x/oauth2/... + +script: + - go test -v golang.org/x/oauth2/... diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/AUTHORS b/Godeps/_workspace/src/golang.org/x/oauth2/AUTHORS new file mode 100644 index 0000000..15167cd --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md b/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md new file mode 100644 index 0000000..46aa2b1 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing to Go + +Go is an open source project. + +It is the work of hundreds of contributors. We appreciate your help! + + +## Filing issues + +When [filing an issue](https://github.com/golang/oauth2/issues), make sure to answer these five questions: + +1. What version of Go are you using (`go version`)? +2. What operating system and processor architecture are you using? +3. What did you do? +4. What did you expect to see? +5. What did you see instead? + +General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. +The gophers there will answer or ask you to file an issue if you've tripped over a bug. + +## Contributing code + +Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) +before sending patches. + +**We do not accept GitHub pull requests** +(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review). + +Unless otherwise noted, the Go source files are distributed under +the BSD-style license found in the LICENSE file. + diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTORS b/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTORS new file mode 100644 index 0000000..1c4577e --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/LICENSE b/Godeps/_workspace/src/golang.org/x/oauth2/LICENSE new file mode 100644 index 0000000..d02f24f --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The oauth2 Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/README.md b/Godeps/_workspace/src/golang.org/x/oauth2/README.md new file mode 100644 index 0000000..0d51417 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/README.md @@ -0,0 +1,64 @@ +# OAuth2 for Go + +[![Build Status](https://travis-ci.org/golang/oauth2.svg?branch=master)](https://travis-ci.org/golang/oauth2) + +oauth2 package contains a client implementation for OAuth 2.0 spec. + +## Installation + +~~~~ +go get golang.org/x/oauth2 +~~~~ + +See godoc for further documentation and examples. + +* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2) +* [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google) + + +## App Engine + +In change 96e89be (March 2015) we removed the `oauth2.Context2` type in favor +of the [`context.Context`](https://golang.org/x/net/context#Context) type from +the `golang.org/x/net/context` package + +This means its no longer possible to use the "Classic App Engine" +`appengine.Context` type with the `oauth2` package. (You're using +Classic App Engine if you import the package `"appengine"`.) + +To work around this, you may use the new `"google.golang.org/appengine"` +package. This package has almost the same API as the `"appengine"` package, +but it can be fetched with `go get` and used on "Managed VMs" and well as +Classic App Engine. + +See the [new `appengine` package's readme](https://github.com/golang/appengine#updating-a-go-app-engine-app) +for information on updating your app. + +If you don't want to update your entire app to use the new App Engine packages, +you may use both sets of packages in parallel, using only the new packages +with the `oauth2` package. + + import ( + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/google" + newappengine "google.golang.org/appengine" + newurlfetch "google.golang.org/appengine/urlfetch" + + "appengine" + ) + + func handler(w http.ResponseWriter, r *http.Request) { + var c appengine.Context = appengine.NewContext(r) + c.Infof("Logging a message with the old package") + + var ctx context.Context = newappengine.NewContext(r) + client := &http.Client{ + Transport: &oauth2.Transport{ + Source: google.AppEngineTokenSource(ctx, "scope"), + Base: &newurlfetch.Transport{Context: ctx}, + }, + } + client.Get("...") + } + diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/bitbucket/bitbucket.go b/Godeps/_workspace/src/golang.org/x/oauth2/bitbucket/bitbucket.go new file mode 100644 index 0000000..44af1f1 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/bitbucket/bitbucket.go @@ -0,0 +1,16 @@ +// Copyright 2015 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package bitbucket provides constants for using OAuth2 to access Bitbucket. +package bitbucket + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Bitbucket's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://bitbucket.org/site/oauth2/authorize", + TokenURL: "https://bitbucket.org/site/oauth2/access_token", +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go b/Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go new file mode 100644 index 0000000..8962c49 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go @@ -0,0 +1,25 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine + +// App Engine hooks. + +package oauth2 + +import ( + "net/http" + + "golang.org/x/net/context" + "golang.org/x/oauth2/internal" + "google.golang.org/appengine/urlfetch" +) + +func init() { + internal.RegisterContextClientFunc(contextClientAppEngine) +} + +func contextClientAppEngine(ctx context.Context) (*http.Client, error) { + return urlfetch.Client(ctx), nil +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/clientcredentials/clientcredentials.go b/Godeps/_workspace/src/golang.org/x/oauth2/clientcredentials/clientcredentials.go new file mode 100644 index 0000000..a47e465 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/clientcredentials/clientcredentials.go @@ -0,0 +1,112 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package clientcredentials implements the OAuth2.0 "client credentials" token flow, +// also known as the "two-legged OAuth 2.0". +// +// This should be used when the client is acting on its own behalf or when the client +// is the resource owner. It may also be used when requesting access to protected +// resources based on an authorization previously arranged with the authorization +// server. +// +// See http://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-4.4 +package clientcredentials + +import ( + "net/http" + "net/url" + "strings" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/internal" +) + +// tokenFromInternal maps an *internal.Token struct into +// an *oauth2.Token struct. +func tokenFromInternal(t *internal.Token) *oauth2.Token { + if t == nil { + return nil + } + tk := &oauth2.Token{ + AccessToken: t.AccessToken, + TokenType: t.TokenType, + RefreshToken: t.RefreshToken, + Expiry: t.Expiry, + } + return tk.WithExtra(t.Raw) +} + +// retrieveToken takes a *Config and uses that to retrieve an *internal.Token. +// This token is then mapped from *internal.Token into an *oauth2.Token which is +// returned along with an error. +func retrieveToken(ctx context.Context, c *Config, v url.Values) (*oauth2.Token, error) { + tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.TokenURL, v) + if err != nil { + return nil, err + } + return tokenFromInternal(tk), nil +} + +// Client Credentials Config describes a 2-legged OAuth2 flow, with both the +// client application information and the server's endpoint URLs. +type Config struct { + // ClientID is the application's ID. + ClientID string + + // ClientSecret is the application's secret. + ClientSecret string + + // TokenURL is the resource server's token endpoint + // URL. This is a constant specific to each server. + TokenURL string + + // Scope specifies optional requested permissions. + Scopes []string +} + +// Token uses client credentials to retrieve a token. +// The HTTP client to use is derived from the context. +// If nil, http.DefaultClient is used. +func (c *Config) Token(ctx context.Context) (*oauth2.Token, error) { + return retrieveToken(ctx, c, url.Values{ + "grant_type": {"client_credentials"}, + "scope": internal.CondVal(strings.Join(c.Scopes, " ")), + }) +} + +// Client returns an HTTP client using the provided token. +// The token will auto-refresh as necessary. The underlying +// HTTP transport will be obtained using the provided context. +// The returned client and its Transport should not be modified. +func (c *Config) Client(ctx context.Context) *http.Client { + return oauth2.NewClient(ctx, c.TokenSource(ctx)) +} + +// TokenSource returns a TokenSource that returns t until t expires, +// automatically refreshing it as necessary using the provided context and the +// client ID and client secret. +// +// Most users will use Config.Client instead. +func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource { + source := &tokenSource{ + ctx: ctx, + conf: c, + } + return oauth2.ReuseTokenSource(nil, source) +} + +type tokenSource struct { + ctx context.Context + conf *Config +} + +// Token refreshes the token by using a new client credentials request. +// tokens received this way do not include a refresh token +func (c *tokenSource) Token() (*oauth2.Token, error) { + return retrieveToken(c.ctx, c.conf, url.Values{ + "grant_type": {"client_credentials"}, + "scope": internal.CondVal(strings.Join(c.conf.Scopes, " ")), + }) +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go b/Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go new file mode 100644 index 0000000..51c1480 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go @@ -0,0 +1,16 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package facebook provides constants for using OAuth2 to access Facebook. +package facebook + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Facebook's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.facebook.com/dialog/oauth", + TokenURL: "https://graph.facebook.com/oauth/access_token", +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/github/github.go b/Godeps/_workspace/src/golang.org/x/oauth2/github/github.go new file mode 100644 index 0000000..26502a3 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/github/github.go @@ -0,0 +1,16 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package github provides constants for using OAuth2 to access Github. +package github + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Github's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://github.com/login/oauth/authorize", + TokenURL: "https://github.com/login/oauth/access_token", +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go new file mode 100644 index 0000000..dc993ef --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go @@ -0,0 +1,86 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "sort" + "strings" + "sync" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2" +) + +// Set at init time by appenginevm_hook.go. If true, we are on App Engine Managed VMs. +var appengineVM bool + +// Set at init time by appengine_hook.go. If nil, we're not on App Engine. +var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error) + +// AppEngineTokenSource returns a token source that fetches tokens +// issued to the current App Engine application's service account. +// If you are implementing a 3-legged OAuth 2.0 flow on App Engine +// that involves user accounts, see oauth2.Config instead. +// +// The provided context must have come from appengine.NewContext. +func AppEngineTokenSource(ctx context.Context, scope ...string) oauth2.TokenSource { + if appengineTokenFunc == nil { + panic("google: AppEngineTokenSource can only be used on App Engine.") + } + scopes := append([]string{}, scope...) + sort.Strings(scopes) + return &appEngineTokenSource{ + ctx: ctx, + scopes: scopes, + key: strings.Join(scopes, " "), + } +} + +// aeTokens helps the fetched tokens to be reused until their expiration. +var ( + aeTokensMu sync.Mutex + aeTokens = make(map[string]*tokenLock) // key is space-separated scopes +) + +type tokenLock struct { + mu sync.Mutex // guards t; held while fetching or updating t + t *oauth2.Token +} + +type appEngineTokenSource struct { + ctx context.Context + scopes []string + key string // to aeTokens map; space-separated scopes +} + +func (ts *appEngineTokenSource) Token() (*oauth2.Token, error) { + if appengineTokenFunc == nil { + panic("google: AppEngineTokenSource can only be used on App Engine.") + } + + aeTokensMu.Lock() + tok, ok := aeTokens[ts.key] + if !ok { + tok = &tokenLock{} + aeTokens[ts.key] = tok + } + aeTokensMu.Unlock() + + tok.mu.Lock() + defer tok.mu.Unlock() + if tok.t.Valid() { + return tok.t, nil + } + access, exp, err := appengineTokenFunc(ts.ctx, ts.scopes...) + if err != nil { + return nil, err + } + tok.t = &oauth2.Token{ + AccessToken: access, + Expiry: exp, + } + return tok.t, nil +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine_hook.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine_hook.go new file mode 100644 index 0000000..4f42c8b --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine_hook.go @@ -0,0 +1,13 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appengine + +package google + +import "google.golang.org/appengine" + +func init() { + appengineTokenFunc = appengine.AccessToken +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/appenginevm_hook.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/appenginevm_hook.go new file mode 100644 index 0000000..633611c --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/appenginevm_hook.go @@ -0,0 +1,14 @@ +// Copyright 2015 The oauth2 Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build appenginevm + +package google + +import "google.golang.org/appengine" + +func init() { + appengineVM = true + appengineTokenFunc = appengine.AccessToken +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/default.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/default.go new file mode 100644 index 0000000..b952362 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/default.go @@ -0,0 +1,155 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "runtime" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/jwt" + "google.golang.org/cloud/compute/metadata" +) + +// DefaultClient returns an HTTP Client that uses the +// DefaultTokenSource to obtain authentication credentials. +// +// This client should be used when developing services +// that run on Google App Engine or Google Compute Engine +// and use "Application Default Credentials." +// +// For more details, see: +// https://developers.google.com/accounts/docs/application-default-credentials +// +func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) { + ts, err := DefaultTokenSource(ctx, scope...) + if err != nil { + return nil, err + } + return oauth2.NewClient(ctx, ts), nil +} + +// DefaultTokenSource is a token source that uses +// "Application Default Credentials". +// +// It looks for credentials in the following places, +// preferring the first location found: +// +// 1. A JSON file whose path is specified by the +// GOOGLE_APPLICATION_CREDENTIALS environment variable. +// 2. A JSON file in a location known to the gcloud command-line tool. +// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json. +// On other systems, $HOME/.config/gcloud/application_default_credentials.json. +// 3. On Google App Engine it uses the appengine.AccessToken function. +// 4. On Google Compute Engine and Google App Engine Managed VMs, it fetches +// credentials from the metadata server. +// (In this final case any provided scopes are ignored.) +// +// For more details, see: +// https://developers.google.com/accounts/docs/application-default-credentials +// +func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) { + // First, try the environment variable. + const envVar = "GOOGLE_APPLICATION_CREDENTIALS" + if filename := os.Getenv(envVar); filename != "" { + ts, err := tokenSourceFromFile(ctx, filename, scope) + if err != nil { + return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err) + } + return ts, nil + } + + // Second, try a well-known file. + filename := wellKnownFile() + _, err := os.Stat(filename) + if err == nil { + ts, err2 := tokenSourceFromFile(ctx, filename, scope) + if err2 == nil { + return ts, nil + } + err = err2 + } else if os.IsNotExist(err) { + err = nil // ignore this error + } + if err != nil { + return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err) + } + + // Third, if we're on Google App Engine use those credentials. + if appengineTokenFunc != nil && !appengineVM { + return AppEngineTokenSource(ctx, scope...), nil + } + + // Fourth, if we're on Google Compute Engine use the metadata server. + if metadata.OnGCE() { + return ComputeTokenSource(""), nil + } + + // None are found; return helpful error. + const url = "https://developers.google.com/accounts/docs/application-default-credentials" + return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url) +} + +func wellKnownFile() string { + const f = "application_default_credentials.json" + if runtime.GOOS == "windows" { + return filepath.Join(os.Getenv("APPDATA"), "gcloud", f) + } + return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f) +} + +func tokenSourceFromFile(ctx context.Context, filename string, scopes []string) (oauth2.TokenSource, error) { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + var d struct { + // Common fields + Type string + ClientID string `json:"client_id"` + + // User Credential fields + ClientSecret string `json:"client_secret"` + RefreshToken string `json:"refresh_token"` + + // Service Account fields + ClientEmail string `json:"client_email"` + PrivateKeyID string `json:"private_key_id"` + PrivateKey string `json:"private_key"` + } + if err := json.Unmarshal(b, &d); err != nil { + return nil, err + } + switch d.Type { + case "authorized_user": + cfg := &oauth2.Config{ + ClientID: d.ClientID, + ClientSecret: d.ClientSecret, + Scopes: append([]string{}, scopes...), // copy + Endpoint: Endpoint, + } + tok := &oauth2.Token{RefreshToken: d.RefreshToken} + return cfg.TokenSource(ctx, tok), nil + case "service_account": + cfg := &jwt.Config{ + Email: d.ClientEmail, + PrivateKey: []byte(d.PrivateKey), + Scopes: append([]string{}, scopes...), // copy + TokenURL: JWTTokenURL, + } + return cfg.TokenSource(ctx), nil + case "": + return nil, errors.New("missing 'type' field in credentials") + default: + return nil, fmt.Errorf("unknown credential type: %q", d.Type) + } +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/google.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/google.go new file mode 100644 index 0000000..0bed738 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/google.go @@ -0,0 +1,145 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package google provides support for making OAuth2 authorized and +// authenticated HTTP requests to Google APIs. +// It supports the Web server flow, client-side credentials, service accounts, +// Google Compute Engine service accounts, and Google App Engine service +// accounts. +// +// For more information, please read +// https://developers.google.com/accounts/docs/OAuth2 +// and +// https://developers.google.com/accounts/docs/application-default-credentials. +package google + +import ( + "encoding/json" + "errors" + "fmt" + "strings" + "time" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/jwt" + "google.golang.org/cloud/compute/metadata" +) + +// Endpoint is Google's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://accounts.google.com/o/oauth2/auth", + TokenURL: "https://accounts.google.com/o/oauth2/token", +} + +// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow. +const JWTTokenURL = "https://accounts.google.com/o/oauth2/token" + +// ConfigFromJSON uses a Google Developers Console client_credentials.json +// file to construct a config. +// client_credentials.json can be downloadable from https://console.developers.google.com, +// under "APIs & Auth" > "Credentials". Download the Web application credentials in the +// JSON format and provide the contents of the file as jsonKey. +func ConfigFromJSON(jsonKey []byte, scope ...string) (*oauth2.Config, error) { + type cred struct { + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + RedirectURIs []string `json:"redirect_uris"` + AuthURI string `json:"auth_uri"` + TokenURI string `json:"token_uri"` + } + var j struct { + Web *cred `json:"web"` + Installed *cred `json:"installed"` + } + if err := json.Unmarshal(jsonKey, &j); err != nil { + return nil, err + } + var c *cred + switch { + case j.Web != nil: + c = j.Web + case j.Installed != nil: + c = j.Installed + default: + return nil, fmt.Errorf("oauth2/google: no credentials found") + } + if len(c.RedirectURIs) < 1 { + return nil, errors.New("oauth2/google: missing redirect URL in the client_credentials.json") + } + return &oauth2.Config{ + ClientID: c.ClientID, + ClientSecret: c.ClientSecret, + RedirectURL: c.RedirectURIs[0], + Scopes: scope, + Endpoint: oauth2.Endpoint{ + AuthURL: c.AuthURI, + TokenURL: c.TokenURI, + }, + }, nil +} + +// JWTConfigFromJSON uses a Google Developers service account JSON key file to read +// the credentials that authorize and authenticate the requests. +// Create a service account on "Credentials" page under "APIs & Auth" for your +// project at https://console.developers.google.com to download a JSON key file. +func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) { + var key struct { + Email string `json:"client_email"` + PrivateKey string `json:"private_key"` + } + if err := json.Unmarshal(jsonKey, &key); err != nil { + return nil, err + } + return &jwt.Config{ + Email: key.Email, + PrivateKey: []byte(key.PrivateKey), + Scopes: scope, + TokenURL: JWTTokenURL, + }, nil +} + +// ComputeTokenSource returns a token source that fetches access tokens +// from Google Compute Engine (GCE)'s metadata server. It's only valid to use +// this token source if your program is running on a GCE instance. +// If no account is specified, "default" is used. +// Further information about retrieving access tokens from the GCE metadata +// server can be found at https://cloud.google.com/compute/docs/authentication. +func ComputeTokenSource(account string) oauth2.TokenSource { + return oauth2.ReuseTokenSource(nil, computeSource{account: account}) +} + +type computeSource struct { + account string +} + +func (cs computeSource) Token() (*oauth2.Token, error) { + if !metadata.OnGCE() { + return nil, errors.New("oauth2/google: can't get a token from the metadata service; not running on GCE") + } + acct := cs.account + if acct == "" { + acct = "default" + } + tokenJSON, err := metadata.Get("instance/service-accounts/" + acct + "/token") + if err != nil { + return nil, err + } + var res struct { + AccessToken string `json:"access_token"` + ExpiresInSec int `json:"expires_in"` + TokenType string `json:"token_type"` + } + err = json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res) + if err != nil { + return nil, fmt.Errorf("oauth2/google: invalid token JSON from metadata: %v", err) + } + if res.ExpiresInSec == 0 || res.AccessToken == "" { + return nil, fmt.Errorf("oauth2/google: incomplete token received from metadata") + } + return &oauth2.Token{ + AccessToken: res.AccessToken, + TokenType: res.TokenType, + Expiry: time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second), + }, nil +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/jwt.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/jwt.go new file mode 100644 index 0000000..b919917 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/jwt.go @@ -0,0 +1,71 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "crypto/rsa" + "fmt" + "time" + + "golang.org/x/oauth2" + "golang.org/x/oauth2/internal" + "golang.org/x/oauth2/jws" +) + +// JWTAccessTokenSourceFromJSON uses a Google Developers service account JSON +// key file to read the credentials that authorize and authenticate the +// requests, and returns a TokenSource that does not use any OAuth2 flow but +// instead creates a JWT and sends that as the access token. +// The audience is typically a URL that specifies the scope of the credentials. +// +// Note that this is not a standard OAuth flow, but rather an +// optimization supported by a few Google services. +// Unless you know otherwise, you should use JWTConfigFromJSON instead. +func JWTAccessTokenSourceFromJSON(jsonKey []byte, audience string) (oauth2.TokenSource, error) { + cfg, err := JWTConfigFromJSON(jsonKey) + if err != nil { + return nil, fmt.Errorf("google: could not parse JSON key: %v", err) + } + pk, err := internal.ParseKey(cfg.PrivateKey) + if err != nil { + return nil, fmt.Errorf("google: could not parse key: %v", err) + } + ts := &jwtAccessTokenSource{ + email: cfg.Email, + audience: audience, + pk: pk, + } + tok, err := ts.Token() + if err != nil { + return nil, err + } + return oauth2.ReuseTokenSource(tok, ts), nil +} + +type jwtAccessTokenSource struct { + email, audience string + pk *rsa.PrivateKey +} + +func (ts *jwtAccessTokenSource) Token() (*oauth2.Token, error) { + iat := time.Now() + exp := iat.Add(time.Hour) + cs := &jws.ClaimSet{ + Iss: ts.email, + Sub: ts.email, + Aud: ts.audience, + Iat: iat.Unix(), + Exp: exp.Unix(), + } + hdr := &jws.Header{ + Algorithm: "RS256", + Typ: "JWT", + } + msg, err := jws.Encode(hdr, cs, ts.pk) + if err != nil { + return nil, fmt.Errorf("google: could not encode JWT: %v", err) + } + return &oauth2.Token{AccessToken: msg, TokenType: "Bearer", Expiry: exp}, nil +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk.go b/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk.go new file mode 100644 index 0000000..d29a3bb --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk.go @@ -0,0 +1,168 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "os" + "os/user" + "path/filepath" + "runtime" + "strings" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/internal" +) + +type sdkCredentials struct { + Data []struct { + Credential struct { + ClientID string `json:"client_id"` + ClientSecret string `json:"client_secret"` + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + TokenExpiry *time.Time `json:"token_expiry"` + } `json:"credential"` + Key struct { + Account string `json:"account"` + Scope string `json:"scope"` + } `json:"key"` + } +} + +// An SDKConfig provides access to tokens from an account already +// authorized via the Google Cloud SDK. +type SDKConfig struct { + conf oauth2.Config + initialToken *oauth2.Token +} + +// NewSDKConfig creates an SDKConfig for the given Google Cloud SDK +// account. If account is empty, the account currently active in +// Google Cloud SDK properties is used. +// Google Cloud SDK credentials must be created by running `gcloud auth` +// before using this function. +// The Google Cloud SDK is available at https://cloud.google.com/sdk/. +func NewSDKConfig(account string) (*SDKConfig, error) { + configPath, err := sdkConfigPath() + if err != nil { + return nil, fmt.Errorf("oauth2/google: error getting SDK config path: %v", err) + } + credentialsPath := filepath.Join(configPath, "credentials") + f, err := os.Open(credentialsPath) + if err != nil { + return nil, fmt.Errorf("oauth2/google: failed to load SDK credentials: %v", err) + } + defer f.Close() + + var c sdkCredentials + if err := json.NewDecoder(f).Decode(&c); err != nil { + return nil, fmt.Errorf("oauth2/google: failed to decode SDK credentials from %q: %v", credentialsPath, err) + } + if len(c.Data) == 0 { + return nil, fmt.Errorf("oauth2/google: no credentials found in %q, run `gcloud auth login` to create one", credentialsPath) + } + if account == "" { + propertiesPath := filepath.Join(configPath, "properties") + f, err := os.Open(propertiesPath) + if err != nil { + return nil, fmt.Errorf("oauth2/google: failed to load SDK properties: %v", err) + } + defer f.Close() + ini, err := internal.ParseINI(f) + if err != nil { + return nil, fmt.Errorf("oauth2/google: failed to parse SDK properties %q: %v", propertiesPath, err) + } + core, ok := ini["core"] + if !ok { + return nil, fmt.Errorf("oauth2/google: failed to find [core] section in %v", ini) + } + active, ok := core["account"] + if !ok { + return nil, fmt.Errorf("oauth2/google: failed to find %q attribute in %v", "account", core) + } + account = active + } + + for _, d := range c.Data { + if account == "" || d.Key.Account == account { + if d.Credential.AccessToken == "" && d.Credential.RefreshToken == "" { + return nil, fmt.Errorf("oauth2/google: no token available for account %q", account) + } + var expiry time.Time + if d.Credential.TokenExpiry != nil { + expiry = *d.Credential.TokenExpiry + } + return &SDKConfig{ + conf: oauth2.Config{ + ClientID: d.Credential.ClientID, + ClientSecret: d.Credential.ClientSecret, + Scopes: strings.Split(d.Key.Scope, " "), + Endpoint: Endpoint, + RedirectURL: "oob", + }, + initialToken: &oauth2.Token{ + AccessToken: d.Credential.AccessToken, + RefreshToken: d.Credential.RefreshToken, + Expiry: expiry, + }, + }, nil + } + } + return nil, fmt.Errorf("oauth2/google: no such credentials for account %q", account) +} + +// Client returns an HTTP client using Google Cloud SDK credentials to +// authorize requests. The token will auto-refresh as necessary. The +// underlying http.RoundTripper will be obtained using the provided +// context. The returned client and its Transport should not be +// modified. +func (c *SDKConfig) Client(ctx context.Context) *http.Client { + return &http.Client{ + Transport: &oauth2.Transport{ + Source: c.TokenSource(ctx), + }, + } +} + +// TokenSource returns an oauth2.TokenSource that retrieve tokens from +// Google Cloud SDK credentials using the provided context. +// It will returns the current access token stored in the credentials, +// and refresh it when it expires, but it won't update the credentials +// with the new access token. +func (c *SDKConfig) TokenSource(ctx context.Context) oauth2.TokenSource { + return c.conf.TokenSource(ctx, c.initialToken) +} + +// Scopes are the OAuth 2.0 scopes the current account is authorized for. +func (c *SDKConfig) Scopes() []string { + return c.conf.Scopes +} + +// sdkConfigPath tries to guess where the gcloud config is located. +// It can be overridden during tests. +var sdkConfigPath = func() (string, error) { + if runtime.GOOS == "windows" { + return filepath.Join(os.Getenv("APPDATA"), "gcloud"), nil + } + homeDir := guessUnixHomeDir() + if homeDir == "" { + return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty") + } + return filepath.Join(homeDir, ".config", "gcloud"), nil +} + +func guessUnixHomeDir() string { + usr, err := user.Current() + if err == nil { + return usr.HomeDir + } + return os.Getenv("HOME") +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go b/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go new file mode 100644 index 0000000..fbe1028 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go @@ -0,0 +1,76 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package internal contains support packages for oauth2 package. +package internal + +import ( + "bufio" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "io" + "strings" +) + +// ParseKey converts the binary contents of a private key file +// to an *rsa.PrivateKey. It detects whether the private key is in a +// PEM container or not. If so, it extracts the the private key +// from PEM container before conversion. It only supports PEM +// containers with no passphrase. +func ParseKey(key []byte) (*rsa.PrivateKey, error) { + block, _ := pem.Decode(key) + if block != nil { + key = block.Bytes + } + parsedKey, err := x509.ParsePKCS8PrivateKey(key) + if err != nil { + parsedKey, err = x509.ParsePKCS1PrivateKey(key) + if err != nil { + return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err) + } + } + parsed, ok := parsedKey.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("private key is invalid") + } + return parsed, nil +} + +func ParseINI(ini io.Reader) (map[string]map[string]string, error) { + result := map[string]map[string]string{ + "": map[string]string{}, // root section + } + scanner := bufio.NewScanner(ini) + currentSection := "" + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if strings.HasPrefix(line, ";") { + // comment. + continue + } + if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { + currentSection = strings.TrimSpace(line[1 : len(line)-1]) + result[currentSection] = map[string]string{} + continue + } + parts := strings.SplitN(line, "=", 2) + if len(parts) == 2 && parts[0] != "" { + result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) + } + } + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error scanning ini: %v", err) + } + return result, nil +} + +func CondVal(v string) []string { + if v == "" { + return nil + } + return []string{v} +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/internal/token.go b/Godeps/_workspace/src/golang.org/x/oauth2/internal/token.go new file mode 100644 index 0000000..39caf6c --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/internal/token.go @@ -0,0 +1,221 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package internal contains support packages for oauth2 package. +package internal + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "mime" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "golang.org/x/net/context" +) + +// Token represents the crendentials used to authorize +// the requests to access protected resources on the OAuth 2.0 +// provider's backend. +// +// This type is a mirror of oauth2.Token and exists to break +// an otherwise-circular dependency. Other internal packages +// should convert this Token into an oauth2.Token before use. +type Token struct { + // AccessToken is the token that authorizes and authenticates + // the requests. + AccessToken string + + // TokenType is the type of token. + // The Type method returns either this or "Bearer", the default. + TokenType string + + // RefreshToken is a token that's used by the application + // (as opposed to the user) to refresh the access token + // if it expires. + RefreshToken string + + // Expiry is the optional expiration time of the access token. + // + // If zero, TokenSource implementations will reuse the same + // token forever and RefreshToken or equivalent + // mechanisms for that TokenSource will not be used. + Expiry time.Time + + // Raw optionally contains extra metadata from the server + // when updating a token. + Raw interface{} +} + +// tokenJSON is the struct representing the HTTP response from OAuth2 +// providers returning a token in JSON form. +type tokenJSON struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + RefreshToken string `json:"refresh_token"` + ExpiresIn expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number + Expires expirationTime `json:"expires"` // broken Facebook spelling of expires_in +} + +func (e *tokenJSON) expiry() (t time.Time) { + if v := e.ExpiresIn; v != 0 { + return time.Now().Add(time.Duration(v) * time.Second) + } + if v := e.Expires; v != 0 { + return time.Now().Add(time.Duration(v) * time.Second) + } + return +} + +type expirationTime int32 + +func (e *expirationTime) UnmarshalJSON(b []byte) error { + var n json.Number + err := json.Unmarshal(b, &n) + if err != nil { + return err + } + i, err := n.Int64() + if err != nil { + return err + } + *e = expirationTime(i) + return nil +} + +var brokenAuthHeaderProviders = []string{ + "https://accounts.google.com/", + "https://api.dropbox.com/", + "https://api.instagram.com/", + "https://api.netatmo.net/", + "https://api.odnoklassniki.ru/", + "https://api.pushbullet.com/", + "https://api.soundcloud.com/", + "https://api.twitch.tv/", + "https://app.box.com/", + "https://connect.stripe.com/", + "https://login.microsoftonline.com/", + "https://login.salesforce.com/", + "https://oauth.sandbox.trainingpeaks.com/", + "https://oauth.trainingpeaks.com/", + "https://oauth.vk.com/", + "https://slack.com/", + "https://test-sandbox.auth.corp.google.com", + "https://test.salesforce.com/", + "https://user.gini.net/", + "https://www.douban.com/", + "https://www.googleapis.com/", + "https://www.linkedin.com/", + "https://www.strava.com/oauth/", +} + +func RegisterBrokenAuthHeaderProvider(tokenURL string) { + brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL) +} + +// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL +// implements the OAuth2 spec correctly +// See https://code.google.com/p/goauth2/issues/detail?id=31 for background. +// In summary: +// - Reddit only accepts client secret in the Authorization header +// - Dropbox accepts either it in URL param or Auth header, but not both. +// - Google only accepts URL param (not spec compliant?), not Auth header +// - Stripe only accepts client secret in Auth header with Bearer method, not Basic +func providerAuthHeaderWorks(tokenURL string) bool { + for _, s := range brokenAuthHeaderProviders { + if strings.HasPrefix(tokenURL, s) { + // Some sites fail to implement the OAuth2 spec fully. + return false + } + } + + // Assume the provider implements the spec properly + // otherwise. We can add more exceptions as they're + // discovered. We will _not_ be adding configurable hooks + // to this package to let users select server bugs. + return true +} + +func RetrieveToken(ctx context.Context, ClientID, ClientSecret, TokenURL string, v url.Values) (*Token, error) { + hc, err := ContextClient(ctx) + if err != nil { + return nil, err + } + v.Set("client_id", ClientID) + bustedAuth := !providerAuthHeaderWorks(TokenURL) + if bustedAuth && ClientSecret != "" { + v.Set("client_secret", ClientSecret) + } + req, err := http.NewRequest("POST", TokenURL, strings.NewReader(v.Encode())) + if err != nil { + return nil, err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + if !bustedAuth { + req.SetBasicAuth(ClientID, ClientSecret) + } + r, err := hc.Do(req) + if err != nil { + return nil, err + } + defer r.Body.Close() + body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20)) + if err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + if code := r.StatusCode; code < 200 || code > 299 { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body) + } + + var token *Token + content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type")) + switch content { + case "application/x-www-form-urlencoded", "text/plain": + vals, err := url.ParseQuery(string(body)) + if err != nil { + return nil, err + } + token = &Token{ + AccessToken: vals.Get("access_token"), + TokenType: vals.Get("token_type"), + RefreshToken: vals.Get("refresh_token"), + Raw: vals, + } + e := vals.Get("expires_in") + if e == "" { + // TODO(jbd): Facebook's OAuth2 implementation is broken and + // returns expires_in field in expires. Remove the fallback to expires, + // when Facebook fixes their implementation. + e = vals.Get("expires") + } + expires, _ := strconv.Atoi(e) + if expires != 0 { + token.Expiry = time.Now().Add(time.Duration(expires) * time.Second) + } + default: + var tj tokenJSON + if err = json.Unmarshal(body, &tj); err != nil { + return nil, err + } + token = &Token{ + AccessToken: tj.AccessToken, + TokenType: tj.TokenType, + RefreshToken: tj.RefreshToken, + Expiry: tj.expiry(), + Raw: make(map[string]interface{}), + } + json.Unmarshal(body, &token.Raw) // no error checks for optional fields + } + // Don't overwrite `RefreshToken` with an empty value + // if this was a token refreshing request. + if token.RefreshToken == "" { + token.RefreshToken = v.Get("refresh_token") + } + return token, nil +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/internal/transport.go b/Godeps/_workspace/src/golang.org/x/oauth2/internal/transport.go new file mode 100644 index 0000000..f1f173e --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/internal/transport.go @@ -0,0 +1,69 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package internal contains support packages for oauth2 package. +package internal + +import ( + "net/http" + + "golang.org/x/net/context" +) + +// HTTPClient is the context key to use with golang.org/x/net/context's +// WithValue function to associate an *http.Client value with a context. +var HTTPClient ContextKey + +// ContextKey is just an empty struct. It exists so HTTPClient can be +// an immutable public variable with a unique type. It's immutable +// because nobody else can create a ContextKey, being unexported. +type ContextKey struct{} + +// ContextClientFunc is a func which tries to return an *http.Client +// given a Context value. If it returns an error, the search stops +// with that error. If it returns (nil, nil), the search continues +// down the list of registered funcs. +type ContextClientFunc func(context.Context) (*http.Client, error) + +var contextClientFuncs []ContextClientFunc + +func RegisterContextClientFunc(fn ContextClientFunc) { + contextClientFuncs = append(contextClientFuncs, fn) +} + +func ContextClient(ctx context.Context) (*http.Client, error) { + if ctx != nil { + if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok { + return hc, nil + } + } + for _, fn := range contextClientFuncs { + c, err := fn(ctx) + if err != nil { + return nil, err + } + if c != nil { + return c, nil + } + } + return http.DefaultClient, nil +} + +func ContextTransport(ctx context.Context) http.RoundTripper { + hc, err := ContextClient(ctx) + // This is a rare error case (somebody using nil on App Engine). + if err != nil { + return ErrorTransport{err} + } + return hc.Transport +} + +// ErrorTransport returns the specified error on RoundTrip. +// This RoundTripper should be used in rare error cases where +// error handling can be postponed to response handling time. +type ErrorTransport struct{ Err error } + +func (t ErrorTransport) RoundTrip(*http.Request) (*http.Response, error) { + return nil, t.Err +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go b/Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go new file mode 100644 index 0000000..b46edb2 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go @@ -0,0 +1,172 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package jws provides encoding and decoding utilities for +// signed JWS messages. +package jws + +import ( + "bytes" + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "strings" + "time" +) + +// ClaimSet contains information about the JWT signature including the +// permissions being requested (scopes), the target of the token, the issuer, +// the time the token was issued, and the lifetime of the token. +type ClaimSet struct { + Iss string `json:"iss"` // email address of the client_id of the application making the access token request + Scope string `json:"scope,omitempty"` // space-delimited list of the permissions the application requests + Aud string `json:"aud"` // descriptor of the intended target of the assertion (Optional). + Exp int64 `json:"exp"` // the expiration time of the assertion (seconds since Unix epoch) + Iat int64 `json:"iat"` // the time the assertion was issued (seconds since Unix epoch) + Typ string `json:"typ,omitempty"` // token type (Optional). + + // Email for which the application is requesting delegated access (Optional). + Sub string `json:"sub,omitempty"` + + // The old name of Sub. Client keeps setting Prn to be + // complaint with legacy OAuth 2.0 providers. (Optional) + Prn string `json:"prn,omitempty"` + + // See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3 + // This array is marshalled using custom code (see (c *ClaimSet) encode()). + PrivateClaims map[string]interface{} `json:"-"` +} + +func (c *ClaimSet) encode() (string, error) { + // Reverting time back for machines whose time is not perfectly in sync. + // If client machine's time is in the future according + // to Google servers, an access token will not be issued. + now := time.Now().Add(-10 * time.Second) + if c.Iat == 0 { + c.Iat = now.Unix() + } + if c.Exp == 0 { + c.Exp = now.Add(time.Hour).Unix() + } + if c.Exp < c.Iat { + return "", fmt.Errorf("jws: invalid Exp = %v; must be later than Iat = %v", c.Exp, c.Iat) + } + + b, err := json.Marshal(c) + if err != nil { + return "", err + } + + if len(c.PrivateClaims) == 0 { + return base64Encode(b), nil + } + + // Marshal private claim set and then append it to b. + prv, err := json.Marshal(c.PrivateClaims) + if err != nil { + return "", fmt.Errorf("jws: invalid map of private claims %v", c.PrivateClaims) + } + + // Concatenate public and private claim JSON objects. + if !bytes.HasSuffix(b, []byte{'}'}) { + return "", fmt.Errorf("jws: invalid JSON %s", b) + } + if !bytes.HasPrefix(prv, []byte{'{'}) { + return "", fmt.Errorf("jws: invalid JSON %s", prv) + } + b[len(b)-1] = ',' // Replace closing curly brace with a comma. + b = append(b, prv[1:]...) // Append private claims. + return base64Encode(b), nil +} + +// Header represents the header for the signed JWS payloads. +type Header struct { + // The algorithm used for signature. + Algorithm string `json:"alg"` + + // Represents the token type. + Typ string `json:"typ"` +} + +func (h *Header) encode() (string, error) { + b, err := json.Marshal(h) + if err != nil { + return "", err + } + return base64Encode(b), nil +} + +// Decode decodes a claim set from a JWS payload. +func Decode(payload string) (*ClaimSet, error) { + // decode returned id token to get expiry + s := strings.Split(payload, ".") + if len(s) < 2 { + // TODO(jbd): Provide more context about the error. + return nil, errors.New("jws: invalid token received") + } + decoded, err := base64Decode(s[1]) + if err != nil { + return nil, err + } + c := &ClaimSet{} + err = json.NewDecoder(bytes.NewBuffer(decoded)).Decode(c) + return c, err +} + +// Signer returns a signature for the given data. +type Signer func(data []byte) (sig []byte, err error) + +// EncodeWithSigner encodes a header and claim set with the provided signer. +func EncodeWithSigner(header *Header, c *ClaimSet, sg Signer) (string, error) { + head, err := header.encode() + if err != nil { + return "", err + } + cs, err := c.encode() + if err != nil { + return "", err + } + ss := fmt.Sprintf("%s.%s", head, cs) + sig, err := sg([]byte(ss)) + if err != nil { + return "", err + } + return fmt.Sprintf("%s.%s", ss, base64Encode(sig)), nil +} + +// Encode encodes a signed JWS with provided header and claim set. +// This invokes EncodeWithSigner using crypto/rsa.SignPKCS1v15 with the given RSA private key. +func Encode(header *Header, c *ClaimSet, key *rsa.PrivateKey) (string, error) { + sg := func(data []byte) (sig []byte, err error) { + h := sha256.New() + h.Write([]byte(data)) + return rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil)) + } + return EncodeWithSigner(header, c, sg) +} + +// base64Encode returns and Base64url encoded version of the input string with any +// trailing "=" stripped. +func base64Encode(b []byte) string { + return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=") +} + +// base64Decode decodes the Base64url encoded string +func base64Decode(s string) ([]byte, error) { + // add back missing padding + switch len(s) % 4 { + case 1: + s += "===" + case 2: + s += "==" + case 3: + s += "=" + } + return base64.URLEncoding.DecodeString(s) +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt.go b/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt.go new file mode 100644 index 0000000..2ffad21 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt.go @@ -0,0 +1,153 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package jwt implements the OAuth 2.0 JSON Web Token flow, commonly +// known as "two-legged OAuth 2.0". +// +// See: https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12 +package jwt + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "strings" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/internal" + "golang.org/x/oauth2/jws" +) + +var ( + defaultGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer" + defaultHeader = &jws.Header{Algorithm: "RS256", Typ: "JWT"} +) + +// Config is the configuration for using JWT to fetch tokens, +// commonly known as "two-legged OAuth 2.0". +type Config struct { + // Email is the OAuth client identifier used when communicating with + // the configured OAuth provider. + Email string + + // PrivateKey contains the contents of an RSA private key or the + // contents of a PEM file that contains a private key. The provided + // private key is used to sign JWT payloads. + // PEM containers with a passphrase are not supported. + // Use the following command to convert a PKCS 12 file into a PEM. + // + // $ openssl pkcs12 -in key.p12 -out key.pem -nodes + // + PrivateKey []byte + + // Subject is the optional user to impersonate. + Subject string + + // Scopes optionally specifies a list of requested permission scopes. + Scopes []string + + // TokenURL is the endpoint required to complete the 2-legged JWT flow. + TokenURL string + + // Expires optionally specifies how long the token is valid for. + Expires time.Duration +} + +// TokenSource returns a JWT TokenSource using the configuration +// in c and the HTTP client from the provided context. +func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource { + return oauth2.ReuseTokenSource(nil, jwtSource{ctx, c}) +} + +// Client returns an HTTP client wrapping the context's +// HTTP transport and adding Authorization headers with tokens +// obtained from c. +// +// The returned client and its Transport should not be modified. +func (c *Config) Client(ctx context.Context) *http.Client { + return oauth2.NewClient(ctx, c.TokenSource(ctx)) +} + +// jwtSource is a source that always does a signed JWT request for a token. +// It should typically be wrapped with a reuseTokenSource. +type jwtSource struct { + ctx context.Context + conf *Config +} + +func (js jwtSource) Token() (*oauth2.Token, error) { + pk, err := internal.ParseKey(js.conf.PrivateKey) + if err != nil { + return nil, err + } + hc := oauth2.NewClient(js.ctx, nil) + claimSet := &jws.ClaimSet{ + Iss: js.conf.Email, + Scope: strings.Join(js.conf.Scopes, " "), + Aud: js.conf.TokenURL, + } + if subject := js.conf.Subject; subject != "" { + claimSet.Sub = subject + // prn is the old name of sub. Keep setting it + // to be compatible with legacy OAuth 2.0 providers. + claimSet.Prn = subject + } + if t := js.conf.Expires; t > 0 { + claimSet.Exp = time.Now().Add(t).Unix() + } + payload, err := jws.Encode(defaultHeader, claimSet, pk) + if err != nil { + return nil, err + } + v := url.Values{} + v.Set("grant_type", defaultGrantType) + v.Set("assertion", payload) + resp, err := hc.PostForm(js.conf.TokenURL, v) + if err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) + if err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + if c := resp.StatusCode; c < 200 || c > 299 { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", resp.Status, body) + } + // tokenRes is the JSON response body. + var tokenRes struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + IDToken string `json:"id_token"` + ExpiresIn int64 `json:"expires_in"` // relative seconds from now + } + if err := json.Unmarshal(body, &tokenRes); err != nil { + return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err) + } + token := &oauth2.Token{ + AccessToken: tokenRes.AccessToken, + TokenType: tokenRes.TokenType, + } + raw := make(map[string]interface{}) + json.Unmarshal(body, &raw) // no error checks for optional fields + token = token.WithExtra(raw) + + if secs := tokenRes.ExpiresIn; secs > 0 { + token.Expiry = time.Now().Add(time.Duration(secs) * time.Second) + } + if v := tokenRes.IDToken; v != "" { + // decode returned id token to get expiry + claimSet, err := jws.Decode(v) + if err != nil { + return nil, fmt.Errorf("oauth2: error decoding JWT token: %v", err) + } + token.Expiry = time.Unix(claimSet.Exp, 0) + } + return token, nil +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go b/Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go new file mode 100644 index 0000000..30c212b --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go @@ -0,0 +1,16 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package linkedin provides constants for using OAuth2 to access LinkedIn. +package linkedin + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is LinkedIn's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.linkedin.com/uas/oauth2/authorization", + TokenURL: "https://www.linkedin.com/uas/oauth2/accessToken", +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/microsoft/microsoft.go b/Godeps/_workspace/src/golang.org/x/oauth2/microsoft/microsoft.go new file mode 100644 index 0000000..a097f81 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/microsoft/microsoft.go @@ -0,0 +1,16 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package microsoft provides constants for using OAuth2 to access Windows Live ID. +package microsoft + +import ( + "golang.org/x/oauth2" +) + +// LiveConnectEndpoint is Windows's Live ID OAuth 2.0 endpoint. +var LiveConnectEndpoint = oauth2.Endpoint{ + AuthURL: "https://login.live.com/oauth20_authorize.srf", + TokenURL: "https://login.live.com/oauth20_token.srf", +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go b/Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go new file mode 100644 index 0000000..9b7b977 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go @@ -0,0 +1,337 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package oauth2 provides support for making +// OAuth2 authorized and authenticated HTTP requests. +// It can additionally grant authorization with Bearer JWT. +package oauth2 + +import ( + "bytes" + "errors" + "net/http" + "net/url" + "strings" + "sync" + + "golang.org/x/net/context" + "golang.org/x/oauth2/internal" +) + +// NoContext is the default context you should supply if not using +// your own context.Context (see https://golang.org/x/net/context). +var NoContext = context.TODO() + +// RegisterBrokenAuthHeaderProvider registers an OAuth2 server +// identified by the tokenURL prefix as an OAuth2 implementation +// which doesn't support the HTTP Basic authentication +// scheme to authenticate with the authorization server. +// Once a server is registered, credentials (client_id and client_secret) +// will be passed as query parameters rather than being present +// in the Authorization header. +// See https://code.google.com/p/goauth2/issues/detail?id=31 for background. +func RegisterBrokenAuthHeaderProvider(tokenURL string) { + internal.RegisterBrokenAuthHeaderProvider(tokenURL) +} + +// Config describes a typical 3-legged OAuth2 flow, with both the +// client application information and the server's endpoint URLs. +type Config struct { + // ClientID is the application's ID. + ClientID string + + // ClientSecret is the application's secret. + ClientSecret string + + // Endpoint contains the resource server's token endpoint + // URLs. These are constants specific to each server and are + // often available via site-specific packages, such as + // google.Endpoint or github.Endpoint. + Endpoint Endpoint + + // RedirectURL is the URL to redirect users going through + // the OAuth flow, after the resource owner's URLs. + RedirectURL string + + // Scope specifies optional requested permissions. + Scopes []string +} + +// A TokenSource is anything that can return a token. +type TokenSource interface { + // Token returns a token or an error. + // Token must be safe for concurrent use by multiple goroutines. + // The returned Token must not be modified. + Token() (*Token, error) +} + +// Endpoint contains the OAuth 2.0 provider's authorization and token +// endpoint URLs. +type Endpoint struct { + AuthURL string + TokenURL string +} + +var ( + // AccessTypeOnline and AccessTypeOffline are options passed + // to the Options.AuthCodeURL method. They modify the + // "access_type" field that gets sent in the URL returned by + // AuthCodeURL. + // + // Online is the default if neither is specified. If your + // application needs to refresh access tokens when the user + // is not present at the browser, then use offline. This will + // result in your application obtaining a refresh token the + // first time your application exchanges an authorization + // code for a user. + AccessTypeOnline AuthCodeOption = SetAuthURLParam("access_type", "online") + AccessTypeOffline AuthCodeOption = SetAuthURLParam("access_type", "offline") + + // ApprovalForce forces the users to view the consent dialog + // and confirm the permissions request at the URL returned + // from AuthCodeURL, even if they've already done so. + ApprovalForce AuthCodeOption = SetAuthURLParam("approval_prompt", "force") +) + +// An AuthCodeOption is passed to Config.AuthCodeURL. +type AuthCodeOption interface { + setValue(url.Values) +} + +type setParam struct{ k, v string } + +func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) } + +// SetAuthURLParam builds an AuthCodeOption which passes key/value parameters +// to a provider's authorization endpoint. +func SetAuthURLParam(key, value string) AuthCodeOption { + return setParam{key, value} +} + +// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page +// that asks for permissions for the required scopes explicitly. +// +// State is a token to protect the user from CSRF attacks. You must +// always provide a non-zero string and validate that it matches the +// the state query parameter on your redirect callback. +// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info. +// +// Opts may include AccessTypeOnline or AccessTypeOffline, as well +// as ApprovalForce. +func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string { + var buf bytes.Buffer + buf.WriteString(c.Endpoint.AuthURL) + v := url.Values{ + "response_type": {"code"}, + "client_id": {c.ClientID}, + "redirect_uri": internal.CondVal(c.RedirectURL), + "scope": internal.CondVal(strings.Join(c.Scopes, " ")), + "state": internal.CondVal(state), + } + for _, opt := range opts { + opt.setValue(v) + } + if strings.Contains(c.Endpoint.AuthURL, "?") { + buf.WriteByte('&') + } else { + buf.WriteByte('?') + } + buf.WriteString(v.Encode()) + return buf.String() +} + +// PasswordCredentialsToken converts a resource owner username and password +// pair into a token. +// +// Per the RFC, this grant type should only be used "when there is a high +// degree of trust between the resource owner and the client (e.g., the client +// is part of the device operating system or a highly privileged application), +// and when other authorization grant types are not available." +// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info. +// +// The HTTP client to use is derived from the context. +// If nil, http.DefaultClient is used. +func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) { + return retrieveToken(ctx, c, url.Values{ + "grant_type": {"password"}, + "username": {username}, + "password": {password}, + "scope": internal.CondVal(strings.Join(c.Scopes, " ")), + }) +} + +// Exchange converts an authorization code into a token. +// +// It is used after a resource provider redirects the user back +// to the Redirect URI (the URL obtained from AuthCodeURL). +// +// The HTTP client to use is derived from the context. +// If a client is not provided via the context, http.DefaultClient is used. +// +// The code will be in the *http.Request.FormValue("code"). Before +// calling Exchange, be sure to validate FormValue("state"). +func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) { + return retrieveToken(ctx, c, url.Values{ + "grant_type": {"authorization_code"}, + "code": {code}, + "redirect_uri": internal.CondVal(c.RedirectURL), + "scope": internal.CondVal(strings.Join(c.Scopes, " ")), + }) +} + +// Client returns an HTTP client using the provided token. +// The token will auto-refresh as necessary. The underlying +// HTTP transport will be obtained using the provided context. +// The returned client and its Transport should not be modified. +func (c *Config) Client(ctx context.Context, t *Token) *http.Client { + return NewClient(ctx, c.TokenSource(ctx, t)) +} + +// TokenSource returns a TokenSource that returns t until t expires, +// automatically refreshing it as necessary using the provided context. +// +// Most users will use Config.Client instead. +func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource { + tkr := &tokenRefresher{ + ctx: ctx, + conf: c, + } + if t != nil { + tkr.refreshToken = t.RefreshToken + } + return &reuseTokenSource{ + t: t, + new: tkr, + } +} + +// tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token" +// HTTP requests to renew a token using a RefreshToken. +type tokenRefresher struct { + ctx context.Context // used to get HTTP requests + conf *Config + refreshToken string +} + +// WARNING: Token is not safe for concurrent access, as it +// updates the tokenRefresher's refreshToken field. +// Within this package, it is used by reuseTokenSource which +// synchronizes calls to this method with its own mutex. +func (tf *tokenRefresher) Token() (*Token, error) { + if tf.refreshToken == "" { + return nil, errors.New("oauth2: token expired and refresh token is not set") + } + + tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{ + "grant_type": {"refresh_token"}, + "refresh_token": {tf.refreshToken}, + }) + + if err != nil { + return nil, err + } + if tf.refreshToken != tk.RefreshToken { + tf.refreshToken = tk.RefreshToken + } + return tk, err +} + +// reuseTokenSource is a TokenSource that holds a single token in memory +// and validates its expiry before each call to retrieve it with +// Token. If it's expired, it will be auto-refreshed using the +// new TokenSource. +type reuseTokenSource struct { + new TokenSource // called when t is expired. + + mu sync.Mutex // guards t + t *Token +} + +// Token returns the current token if it's still valid, else will +// refresh the current token (using r.Context for HTTP client +// information) and return the new one. +func (s *reuseTokenSource) Token() (*Token, error) { + s.mu.Lock() + defer s.mu.Unlock() + if s.t.Valid() { + return s.t, nil + } + t, err := s.new.Token() + if err != nil { + return nil, err + } + s.t = t + return t, nil +} + +// StaticTokenSource returns a TokenSource that always returns the same token. +// Because the provided token t is never refreshed, StaticTokenSource is only +// useful for tokens that never expire. +func StaticTokenSource(t *Token) TokenSource { + return staticTokenSource{t} +} + +// staticTokenSource is a TokenSource that always returns the same Token. +type staticTokenSource struct { + t *Token +} + +func (s staticTokenSource) Token() (*Token, error) { + return s.t, nil +} + +// HTTPClient is the context key to use with golang.org/x/net/context's +// WithValue function to associate an *http.Client value with a context. +var HTTPClient internal.ContextKey + +// NewClient creates an *http.Client from a Context and TokenSource. +// The returned client is not valid beyond the lifetime of the context. +// +// As a special case, if src is nil, a non-OAuth2 client is returned +// using the provided context. This exists to support related OAuth2 +// packages. +func NewClient(ctx context.Context, src TokenSource) *http.Client { + if src == nil { + c, err := internal.ContextClient(ctx) + if err != nil { + return &http.Client{Transport: internal.ErrorTransport{err}} + } + return c + } + return &http.Client{ + Transport: &Transport{ + Base: internal.ContextTransport(ctx), + Source: ReuseTokenSource(nil, src), + }, + } +} + +// ReuseTokenSource returns a TokenSource which repeatedly returns the +// same token as long as it's valid, starting with t. +// When its cached token is invalid, a new token is obtained from src. +// +// ReuseTokenSource is typically used to reuse tokens from a cache +// (such as a file on disk) between runs of a program, rather than +// obtaining new tokens unnecessarily. +// +// The initial token t may be nil, in which case the TokenSource is +// wrapped in a caching version if it isn't one already. This also +// means it's always safe to wrap ReuseTokenSource around any other +// TokenSource without adverse effects. +func ReuseTokenSource(t *Token, src TokenSource) TokenSource { + // Don't wrap a reuseTokenSource in itself. That would work, + // but cause an unnecessary number of mutex operations. + // Just build the equivalent one. + if rt, ok := src.(*reuseTokenSource); ok { + if t == nil { + // Just use it directly. + return rt + } + src = rt.new + } + return &reuseTokenSource{ + t: t, + new: src, + } +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go b/Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go new file mode 100644 index 0000000..60741ce --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go @@ -0,0 +1,16 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package odnoklassniki provides constants for using OAuth2 to access Odnoklassniki. +package odnoklassniki + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is Odnoklassniki's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.odnoklassniki.ru/oauth/authorize", + TokenURL: "https://api.odnoklassniki.ru/oauth/token.do", +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go b/Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go new file mode 100644 index 0000000..3230832 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go @@ -0,0 +1,22 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package paypal provides constants for using OAuth2 to access PayPal. +package paypal + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is PayPal's OAuth 2.0 endpoint in live (production) environment. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize", + TokenURL: "https://api.paypal.com/v1/identity/openidconnect/tokenservice", +} + +// SandboxEndpoint is PayPal's OAuth 2.0 endpoint in sandbox (testing) environment. +var SandboxEndpoint = oauth2.Endpoint{ + AuthURL: "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize", + TokenURL: "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice", +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/token.go b/Godeps/_workspace/src/golang.org/x/oauth2/token.go new file mode 100644 index 0000000..7a3167f --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/token.go @@ -0,0 +1,158 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package oauth2 + +import ( + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2/internal" +) + +// expiryDelta determines how earlier a token should be considered +// expired than its actual expiration time. It is used to avoid late +// expirations due to client-server time mismatches. +const expiryDelta = 10 * time.Second + +// Token represents the crendentials used to authorize +// the requests to access protected resources on the OAuth 2.0 +// provider's backend. +// +// Most users of this package should not access fields of Token +// directly. They're exported mostly for use by related packages +// implementing derivative OAuth2 flows. +type Token struct { + // AccessToken is the token that authorizes and authenticates + // the requests. + AccessToken string `json:"access_token"` + + // TokenType is the type of token. + // The Type method returns either this or "Bearer", the default. + TokenType string `json:"token_type,omitempty"` + + // RefreshToken is a token that's used by the application + // (as opposed to the user) to refresh the access token + // if it expires. + RefreshToken string `json:"refresh_token,omitempty"` + + // Expiry is the optional expiration time of the access token. + // + // If zero, TokenSource implementations will reuse the same + // token forever and RefreshToken or equivalent + // mechanisms for that TokenSource will not be used. + Expiry time.Time `json:"expiry,omitempty"` + + // raw optionally contains extra metadata from the server + // when updating a token. + raw interface{} +} + +// Type returns t.TokenType if non-empty, else "Bearer". +func (t *Token) Type() string { + if strings.EqualFold(t.TokenType, "bearer") { + return "Bearer" + } + if strings.EqualFold(t.TokenType, "mac") { + return "MAC" + } + if strings.EqualFold(t.TokenType, "basic") { + return "Basic" + } + if t.TokenType != "" { + return t.TokenType + } + return "Bearer" +} + +// SetAuthHeader sets the Authorization header to r using the access +// token in t. +// +// This method is unnecessary when using Transport or an HTTP Client +// returned by this package. +func (t *Token) SetAuthHeader(r *http.Request) { + r.Header.Set("Authorization", t.Type()+" "+t.AccessToken) +} + +// WithExtra returns a new Token that's a clone of t, but using the +// provided raw extra map. This is only intended for use by packages +// implementing derivative OAuth2 flows. +func (t *Token) WithExtra(extra interface{}) *Token { + t2 := new(Token) + *t2 = *t + t2.raw = extra + return t2 +} + +// Extra returns an extra field. +// Extra fields are key-value pairs returned by the server as a +// part of the token retrieval response. +func (t *Token) Extra(key string) interface{} { + if raw, ok := t.raw.(map[string]interface{}); ok { + return raw[key] + } + + vals, ok := t.raw.(url.Values) + if !ok { + return nil + } + + v := vals.Get(key) + switch s := strings.TrimSpace(v); strings.Count(s, ".") { + case 0: // Contains no "."; try to parse as int + if i, err := strconv.ParseInt(s, 10, 64); err == nil { + return i + } + case 1: // Contains a single "."; try to parse as float + if f, err := strconv.ParseFloat(s, 64); err == nil { + return f + } + } + + return v +} + +// expired reports whether the token is expired. +// t must be non-nil. +func (t *Token) expired() bool { + if t.Expiry.IsZero() { + return false + } + return t.Expiry.Add(-expiryDelta).Before(time.Now()) +} + +// Valid reports whether t is non-nil, has an AccessToken, and is not expired. +func (t *Token) Valid() bool { + return t != nil && t.AccessToken != "" && !t.expired() +} + +// tokenFromInternal maps an *internal.Token struct into +// a *Token struct. +func tokenFromInternal(t *internal.Token) *Token { + if t == nil { + return nil + } + return &Token{ + AccessToken: t.AccessToken, + TokenType: t.TokenType, + RefreshToken: t.RefreshToken, + Expiry: t.Expiry, + raw: t.Raw, + } +} + +// retrieveToken takes a *Config and uses that to retrieve an *internal.Token. +// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along +// with an error.. +func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) { + tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v) + if err != nil { + return nil, err + } + return tokenFromInternal(tk), nil +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/transport.go b/Godeps/_workspace/src/golang.org/x/oauth2/transport.go new file mode 100644 index 0000000..92ac7e2 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/transport.go @@ -0,0 +1,132 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package oauth2 + +import ( + "errors" + "io" + "net/http" + "sync" +) + +// Transport is an http.RoundTripper that makes OAuth 2.0 HTTP requests, +// wrapping a base RoundTripper and adding an Authorization header +// with a token from the supplied Sources. +// +// Transport is a low-level mechanism. Most code will use the +// higher-level Config.Client method instead. +type Transport struct { + // Source supplies the token to add to outgoing requests' + // Authorization headers. + Source TokenSource + + // Base is the base RoundTripper used to make HTTP requests. + // If nil, http.DefaultTransport is used. + Base http.RoundTripper + + mu sync.Mutex // guards modReq + modReq map[*http.Request]*http.Request // original -> modified +} + +// RoundTrip authorizes and authenticates the request with an +// access token. If no token exists or token is expired, +// tries to refresh/fetch a new token. +func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { + if t.Source == nil { + return nil, errors.New("oauth2: Transport's Source is nil") + } + token, err := t.Source.Token() + if err != nil { + return nil, err + } + + req2 := cloneRequest(req) // per RoundTripper contract + token.SetAuthHeader(req2) + t.setModReq(req, req2) + res, err := t.base().RoundTrip(req2) + if err != nil { + t.setModReq(req, nil) + return nil, err + } + res.Body = &onEOFReader{ + rc: res.Body, + fn: func() { t.setModReq(req, nil) }, + } + return res, nil +} + +// CancelRequest cancels an in-flight request by closing its connection. +func (t *Transport) CancelRequest(req *http.Request) { + type canceler interface { + CancelRequest(*http.Request) + } + if cr, ok := t.base().(canceler); ok { + t.mu.Lock() + modReq := t.modReq[req] + delete(t.modReq, req) + t.mu.Unlock() + cr.CancelRequest(modReq) + } +} + +func (t *Transport) base() http.RoundTripper { + if t.Base != nil { + return t.Base + } + return http.DefaultTransport +} + +func (t *Transport) setModReq(orig, mod *http.Request) { + t.mu.Lock() + defer t.mu.Unlock() + if t.modReq == nil { + t.modReq = make(map[*http.Request]*http.Request) + } + if mod == nil { + delete(t.modReq, orig) + } else { + t.modReq[orig] = mod + } +} + +// cloneRequest returns a clone of the provided *http.Request. +// The clone is a shallow copy of the struct and its Header map. +func cloneRequest(r *http.Request) *http.Request { + // shallow copy of the struct + r2 := new(http.Request) + *r2 = *r + // deep copy of the Header + r2.Header = make(http.Header, len(r.Header)) + for k, s := range r.Header { + r2.Header[k] = append([]string(nil), s...) + } + return r2 +} + +type onEOFReader struct { + rc io.ReadCloser + fn func() +} + +func (r *onEOFReader) Read(p []byte) (n int, err error) { + n, err = r.rc.Read(p) + if err == io.EOF { + r.runFunc() + } + return +} + +func (r *onEOFReader) Close() error { + err := r.rc.Close() + r.runFunc() + return err +} + +func (r *onEOFReader) runFunc() { + if fn := r.fn; fn != nil { + fn() + r.fn = nil + } +} diff --git a/Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go b/Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go new file mode 100644 index 0000000..6463482 --- /dev/null +++ b/Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go @@ -0,0 +1,16 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package vk provides constants for using OAuth2 to access VK.com. +package vk + +import ( + "golang.org/x/oauth2" +) + +// Endpoint is VK's OAuth 2.0 endpoint. +var Endpoint = oauth2.Endpoint{ + AuthURL: "https://oauth.vk.com/authorize", + TokenURL: "https://oauth.vk.com/access_token", +}