This commit is contained in:
Alex Suraci
2016-01-30 11:45:07 -08:00
parent 13faed5472
commit edcd5c99e4
116 changed files with 3353 additions and 1454 deletions

29
Godeps/Godeps.json generated
View File

@@ -1,22 +1,21 @@
{ {
"ImportPath": "github.com/concourse/github-release-resource", "ImportPath": "github.com/concourse/github-release-resource",
"GoVersion": "go1.5.1", "GoVersion": "go1.5",
"Packages": [ "Packages": [
"./..." "./..."
], ],
"Deps": [ "Deps": [
{
"ImportPath": "code.google.com/p/goauth2/oauth",
"Comment": "weekly-57",
"Rev": "'222e66a2882ef45c3153c86c0aa82fe65fc29a78'"
},
{ {
"ImportPath": "github.com/cppforlife/go-semi-semantic/version", "ImportPath": "github.com/cppforlife/go-semi-semantic/version",
"Rev": "a7ba07c61e58e6df7693d49a13d24f533182518a" "Rev": "a7ba07c61e58e6df7693d49a13d24f533182518a"
}, },
{ {
"ImportPath": "github.com/golang/protobuf/proto", "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", "ImportPath": "github.com/google/go-querystring/query",
@@ -28,17 +27,21 @@
}, },
{ {
"ImportPath": "github.com/onsi/ginkgo", "ImportPath": "github.com/onsi/ginkgo",
"Comment": "v1.2.0-22-g39d2c24", "Comment": "v1.2.0-36-g3341026",
"Rev": "39d2c24f8a92c88f7e7f4d8ec6c80d3cc8f5ac65" "Rev": "33410268c3881642c746ada23c04eeb331212596"
}, },
{ {
"ImportPath": "github.com/onsi/gomega", "ImportPath": "github.com/onsi/gomega",
"Comment": "v1.0-71-g2152b45", "Comment": "v1.0-77-g28f13bc",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28" "Rev": "28f13bc54cf0f72e3fa395ba9d3added0b3b849c"
}, },
{ {
"ImportPath": "github.com/zachgersh/go-github/github", "ImportPath": "golang.org/x/net/context",
"Rev": "f47a8b33261f10482dfede3cc839c4fcf34da04f" "Rev": "04b9de9b512f58addf28c9853d50ebef61c3953e"
},
{
"ImportPath": "golang.org/x/oauth2",
"Rev": "8a57ed94ffd43444c0879fe75701732a38afc985"
} }
] ]
} }

View File

@@ -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()
}

View File

@@ -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"}}

View File

@@ -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: <No 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-----

View File

@@ -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)
}

View File

@@ -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 <key.p12> -nocerts -passin pass:notasecret -nodes -out <key.pem>
//
// 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
}

View File

@@ -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
}

View File

@@ -30,7 +30,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Protocol buffer deep copy and merge. // Protocol buffer deep copy and merge.
// TODO: MessageSet and RawMessage. // TODO: RawMessage.
package proto package proto

View File

@@ -105,6 +105,11 @@ func (p *Buffer) EncodeVarint(x uint64) error {
return nil 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) { func sizeVarint(x uint64) (n int) {
for { for {
n++ n++
@@ -1248,24 +1253,9 @@ func size_struct(prop *StructProperties, base structPointer) (n int) {
} }
// Factor in any oneof fields. // Factor in any oneof fields.
// TODO: This could be faster and use less reflection. if prop.oneofSizer != nil {
if prop.oneofMarshaler != nil { m := structPointer_Interface(base, prop.stype).(Message)
sv := reflect.ValueOf(structPointer_Interface(base, prop.stype)).Elem() n += prop.oneofSizer(m)
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))
}
} }
return return

View File

@@ -30,7 +30,6 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Protocol buffer comparison. // Protocol buffer comparison.
// TODO: MessageSet.
package proto package proto
@@ -51,7 +50,9 @@ Equality is defined in this way:
are equal, and extensions sets are equal. are equal, and extensions sets are equal.
- Two set scalar fields are equal iff their values are equal. - Two set scalar fields are equal iff their values are equal.
If the fields are of a floating-point type, remember that 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, - Two repeated fields are equal iff their lengths are the same,
and their corresponding elements are equal (a "bytes" field, and their corresponding elements are equal (a "bytes" field,
although represented by []byte, is not a repeated 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. // v1 and v2 are known to have the same type.
func equalStruct(v1, v2 reflect.Value) bool { func equalStruct(v1, v2 reflect.Value) bool {
sprop := GetProperties(v1.Type())
for i := 0; i < v1.NumField(); i++ { for i := 0; i < v1.NumField(); i++ {
f := v1.Type().Field(i) f := v1.Type().Field(i)
if strings.HasPrefix(f.Name, "XXX_") { if strings.HasPrefix(f.Name, "XXX_") {
@@ -114,7 +116,7 @@ func equalStruct(v1, v2 reflect.Value) bool {
} }
f1, f2 = f1.Elem(), f2.Elem() f1, f2 = f1.Elem(), f2.Elem()
} }
if !equalAny(f1, f2) { if !equalAny(f1, f2, sprop.Prop[i]) {
return false return false
} }
} }
@@ -141,7 +143,8 @@ func equalStruct(v1, v2 reflect.Value) bool {
} }
// v1 and v2 are known to have the same type. // 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 { if v1.Type() == protoMessageType {
m1, _ := v1.Interface().(Message) m1, _ := v1.Interface().(Message)
m2, _ := v2.Interface().(Message) m2, _ := v2.Interface().(Message)
@@ -164,7 +167,7 @@ func equalAny(v1, v2 reflect.Value) bool {
if e1.Type() != e2.Type() { if e1.Type() != e2.Type() {
return false return false
} }
return equalAny(e1, e2) return equalAny(e1, e2, nil)
case reflect.Map: case reflect.Map:
if v1.Len() != v2.Len() { if v1.Len() != v2.Len() {
return false return false
@@ -175,16 +178,22 @@ func equalAny(v1, v2 reflect.Value) bool {
// This key was not found in the second map. // This key was not found in the second map.
return false return false
} }
if !equalAny(v1.MapIndex(key), val2) { if !equalAny(v1.MapIndex(key), val2, nil) {
return false return false
} }
} }
return true return true
case reflect.Ptr: case reflect.Ptr:
return equalAny(v1.Elem(), v2.Elem()) return equalAny(v1.Elem(), v2.Elem(), prop)
case reflect.Slice: case reflect.Slice:
if v1.Type().Elem().Kind() == reflect.Uint8 { if v1.Type().Elem().Kind() == reflect.Uint8 {
// short circuit: []byte // 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() { if v1.IsNil() != v2.IsNil() {
return false return false
} }
@@ -195,7 +204,7 @@ func equalAny(v1, v2 reflect.Value) bool {
return false return false
} }
for i := 0; i < v1.Len(); i++ { 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 return false
} }
} }
@@ -230,7 +239,7 @@ func equalExtensions(base reflect.Type, em1, em2 map[int32]Extension) bool {
if m1 != nil && m2 != nil { if m1 != nil && m2 != nil {
// Both are unencoded. // Both are unencoded.
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2)) { if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
return false return false
} }
continue 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) log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err)
return false return false
} }
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2)) { if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
return false return false
} }
} }

View File

@@ -301,7 +301,6 @@ func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
o := NewBuffer(b) o := NewBuffer(b)
t := reflect.TypeOf(extension.ExtensionType) t := reflect.TypeOf(extension.ExtensionType)
rep := extension.repeated()
props := extensionProperties(extension) props := extensionProperties(extension)
@@ -323,7 +322,7 @@ func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
return nil, err return nil, err
} }
if !rep || o.index >= len(o.buf) { if o.index >= len(o.buf) {
break break
} }
} }

View File

@@ -70,6 +70,12 @@ for a protocol buffer variable v:
with distinguished wrapper types for each possible field value. with distinguished wrapper types for each possible field value.
- Marshal and Unmarshal are functions to encode and decode the wire format. - 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. The simplest way to describe this is to see an example.
Given file test.proto, containing Given file test.proto, containing
@@ -229,6 +235,7 @@ To create and play with a Test object:
test := &pb.Test{ test := &pb.Test{
Label: proto.String("hello"), Label: proto.String("hello"),
Type: proto.Int32(17), Type: proto.Int32(17),
Reps: []int64{1, 2, 3},
Optionalgroup: &pb.Test_OptionalGroup{ Optionalgroup: &pb.Test_OptionalGroup{
RequiredField: proto.String("good bye"), RequiredField: proto.String("good bye"),
}, },
@@ -881,3 +888,7 @@ func isProto3Zero(v reflect.Value) bool {
} }
return false 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

View File

@@ -44,11 +44,11 @@ import (
"sort" "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. // 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: // model what the protocol compiler produces for the following protocol message:
// message MessageSet { // message MessageSet {
// repeated group Item = 1 { // 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 // 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. // because that would introduce a circular dependency between it and this package.
//
// When a proto1 proto has a field that looks like:
// optional message<MessageSet> 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 { type _MessageSet_Item struct {
TypeId *int32 `protobuf:"varint,2,req,name=type_id"` TypeId *int32 `protobuf:"varint,2,req,name=type_id"`
Message []byte `protobuf:"bytes,3,req,name=message"` Message []byte `protobuf:"bytes,3,req,name=message"`
} }
type MessageSet struct { type messageSet struct {
Item []*_MessageSet_Item `protobuf:"group,1,rep"` Item []*_MessageSet_Item `protobuf:"group,1,rep"`
XXX_unrecognized []byte XXX_unrecognized []byte
// TODO: caching? // TODO: caching?
} }
// Make sure MessageSet is a Message. // Make sure messageSet is a Message.
var _ Message = (*MessageSet)(nil) var _ Message = (*messageSet)(nil)
// messageTypeIder is an interface satisfied by a protocol buffer type // messageTypeIder is an interface satisfied by a protocol buffer type
// that may be stored in a MessageSet. // that may be stored in a MessageSet.
@@ -86,7 +79,7 @@ type messageTypeIder interface {
MessageTypeId() int32 MessageTypeId() int32
} }
func (ms *MessageSet) find(pb Message) *_MessageSet_Item { func (ms *messageSet) find(pb Message) *_MessageSet_Item {
mti, ok := pb.(messageTypeIder) mti, ok := pb.(messageTypeIder)
if !ok { if !ok {
return nil return nil
@@ -100,24 +93,24 @@ func (ms *MessageSet) find(pb Message) *_MessageSet_Item {
return nil return nil
} }
func (ms *MessageSet) Has(pb Message) bool { func (ms *messageSet) Has(pb Message) bool {
if ms.find(pb) != nil { if ms.find(pb) != nil {
return true return true
} }
return false return false
} }
func (ms *MessageSet) Unmarshal(pb Message) error { func (ms *messageSet) Unmarshal(pb Message) error {
if item := ms.find(pb); item != nil { if item := ms.find(pb); item != nil {
return Unmarshal(item.Message, pb) return Unmarshal(item.Message, pb)
} }
if _, ok := pb.(messageTypeIder); !ok { if _, ok := pb.(messageTypeIder); !ok {
return ErrNoMessageTypeId return errNoMessageTypeID
} }
return nil // TODO: return error instead? return nil // TODO: return error instead?
} }
func (ms *MessageSet) Marshal(pb Message) error { func (ms *messageSet) Marshal(pb Message) error {
msg, err := Marshal(pb) msg, err := Marshal(pb)
if err != nil { if err != nil {
return err return err
@@ -130,7 +123,7 @@ func (ms *MessageSet) Marshal(pb Message) error {
mti, ok := pb.(messageTypeIder) mti, ok := pb.(messageTypeIder)
if !ok { if !ok {
return ErrNoMessageTypeId return errNoMessageTypeID
} }
mtid := mti.MessageTypeId() mtid := mti.MessageTypeId()
@@ -141,9 +134,9 @@ func (ms *MessageSet) Marshal(pb Message) error {
return nil return nil
} }
func (ms *MessageSet) Reset() { *ms = MessageSet{} } func (ms *messageSet) Reset() { *ms = messageSet{} }
func (ms *MessageSet) String() string { return CompactTextString(ms) } func (ms *messageSet) String() string { return CompactTextString(ms) }
func (*MessageSet) ProtoMessage() {} func (*messageSet) ProtoMessage() {}
// Support for the message_set_wire_format message option. // Support for the message_set_wire_format message option.
@@ -169,7 +162,7 @@ func MarshalMessageSet(m map[int32]Extension) ([]byte, error) {
} }
sort.Ints(ids) 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 { for _, id := range ids {
e := m[int32(id)] e := m[int32(id)]
// Remove the wire type and field number varint, as well as the length varint. // 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. // 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. // 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 { func UnmarshalMessageSet(buf []byte, m map[int32]Extension) error {
ms := new(MessageSet) ms := new(messageSet)
if err := Unmarshal(buf, ms); err != nil { if err := Unmarshal(buf, ms); err != nil {
return err return err
} }

View File

@@ -91,6 +91,9 @@ type oneofMarshaler func(Message, *Buffer) error
// A oneofUnmarshaler does the unmarshaling for a oneof field in a message. // A oneofUnmarshaler does the unmarshaling for a oneof field in a message.
type oneofUnmarshaler func(Message, int, int, *Buffer) (bool, error) 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 // 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 // use-cases. Encoded protocol buffers are often in tag order with small tag
// numbers. // numbers.
@@ -142,6 +145,7 @@ type StructProperties struct {
oneofMarshaler oneofMarshaler oneofMarshaler oneofMarshaler
oneofUnmarshaler oneofUnmarshaler oneofUnmarshaler oneofUnmarshaler
oneofSizer oneofSizer
stype reflect.Type stype reflect.Type
// OneofTypes contains information about the oneof fields in this message. // OneofTypes contains information about the oneof fields in this message.
@@ -712,11 +716,11 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
sort.Sort(prop) sort.Sort(prop)
type oneofMessage interface { 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 { if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
var oots []interface{} var oots []interface{}
prop.oneofMarshaler, prop.oneofUnmarshaler, oots = om.XXX_OneofFuncs() prop.oneofMarshaler, prop.oneofUnmarshaler, prop.oneofSizer, oots = om.XXX_OneofFuncs()
prop.stype = t prop.stype = t
// Interpret oneof metadata. // Interpret oneof metadata.

View File

@@ -170,20 +170,12 @@ func writeName(w *textWriter, props *Properties) error {
return nil return nil
} }
var (
messageSetType = reflect.TypeOf((*MessageSet)(nil)).Elem()
)
// raw is the interface satisfied by RawMessage. // raw is the interface satisfied by RawMessage.
type raw interface { type raw interface {
Bytes() []byte Bytes() []byte
} }
func writeStruct(w *textWriter, sv reflect.Value) error { func writeStruct(w *textWriter, sv reflect.Value) error {
if sv.Type() == messageSetType {
return writeMessageSet(w, sv.Addr().Interface().(*MessageSet))
}
st := sv.Type() st := sv.Type()
sprops := GetProperties(st) sprops := GetProperties(st)
for i := 0; i < sv.NumField(); i++ { for i := 0; i < sv.NumField(); i++ {
@@ -525,44 +517,6 @@ func writeString(w *textWriter, s string) error {
return w.WriteByte('"') 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) { func writeUnknownStruct(w *textWriter, data []byte) (err error) {
if !w.compact { if !w.compact {
if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil { if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil {

View File

@@ -249,11 +249,11 @@ func (s *ActivityService) ListEventsPerformedByUser(user string, publicOnly bool
return *events, resp, err 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. // true, only public events will be returned.
// //
// GitHub API docs: http://developer.github.com/v3/activity/events/#list-events-that-a-user-has-received // 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 var u string
if publicOnly { if publicOnly {
u = fmt.Sprintf("users/%v/received_events/public", user) u = fmt.Sprintf("users/%v/received_events/public", user)

View File

@@ -41,6 +41,7 @@ type NotificationListOptions struct {
All bool `url:"all,omitempty"` All bool `url:"all,omitempty"`
Participating bool `url:"participating,omitempty"` Participating bool `url:"participating,omitempty"`
Since time.Time `url:"since,omitempty"` Since time.Time `url:"since,omitempty"`
Before time.Time `url:"before,omitempty"`
} }
// ListNotifications lists all notifications for the authenticated user. // ListNotifications lists all notifications for the authenticated user.

View File

@@ -50,13 +50,18 @@ func (s *ActivityService) ListWatchers(owner, repo string, opt *ListOptions) ([]
// the empty string will fetch watched repos for the authenticated user. // 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 // 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 var u string
if user != "" { if user != "" {
u = fmt.Sprintf("users/%v/subscriptions", user) u = fmt.Sprintf("users/%v/subscriptions", user)
} else { } else {
u = "user/subscriptions" u = "user/subscriptions"
} }
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil) req, err := s.client.NewRequest("GET", u, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@@ -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 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 application. go-github does not handle conditional requests directly, but is
instead designed to work with a caching http.Transport. We recommend using 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 https://github.com/sourcegraph/apiproxy to provide additional flexibility and
control of caching rules. control of caching rules.

View File

@@ -33,7 +33,7 @@ func (s *GitService) GetBlob(owner string, repo string, sha string) (*Blob, *Res
// CreateBlob creates a blob object. // 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) { func (s *GitService) CreateBlob(owner string, repo string, blob *Blob) (*Blob, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/git/blobs", owner, repo) u := fmt.Sprintf("repos/%v/%v/git/blobs", owner, repo)
req, err := s.client.NewRequest("POST", u, blob) req, err := s.client.NewRequest("POST", u, blob)

View File

@@ -17,6 +17,7 @@ import (
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"github.com/google/go-querystring/query" "github.com/google/go-querystring/query"
@@ -31,6 +32,7 @@ const (
headerRateLimit = "X-RateLimit-Limit" headerRateLimit = "X-RateLimit-Limit"
headerRateRemaining = "X-RateLimit-Remaining" headerRateRemaining = "X-RateLimit-Remaining"
headerRateReset = "X-RateLimit-Reset" headerRateReset = "X-RateLimit-Reset"
headerOTP = "X-GitHub-OTP"
mediaTypeV3 = "application/vnd.github.v3+json" mediaTypeV3 = "application/vnd.github.v3+json"
defaultMediaType = "application/octet-stream" defaultMediaType = "application/octet-stream"
@@ -46,6 +48,9 @@ const (
// https://developer.github.com/changes/2015-06-24-api-enhancements-for-working-with-organization-permissions/ // https://developer.github.com/changes/2015-06-24-api-enhancements-for-working-with-organization-permissions/
mediaTypeOrgPermissionPreview = "application/vnd.github.ironman-preview+json" mediaTypeOrgPermissionPreview = "application/vnd.github.ironman-preview+json"
mediaTypeOrgPermissionRepoPreview = "application/vnd.github.ironman-preview.repository+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. // A Client manages communication with the GitHub API.
@@ -64,11 +69,8 @@ type Client struct {
// User agent used when communicating with the GitHub API. // User agent used when communicating with the GitHub API.
UserAgent string UserAgent string
// Rate specifies the current rate limit for the client as determined by the rateMu sync.Mutex
// most recent API call. If the client is used in a multi-user application, rate Rate
// this rate may not always be up-to-date. Call RateLimit() to check the
// current rate.
Rate Rate
// Services used for talking to different parts of the GitHub API. // Services used for talking to different parts of the GitHub API.
Activity *ActivityService 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 // 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 // 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 // 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) response := newResponse(resp)
c.Rate = response.Rate c.rateMu.Lock()
c.rate = response.Rate
c.rateMu.Unlock()
err = CheckResponse(resp) err = CheckResponse(resp)
if err != nil { if err != nil {
@@ -321,6 +336,9 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
io.Copy(w, resp.Body) io.Copy(w, resp.Body)
} else { } else {
err = json.NewDecoder(resp.Body).Decode(v) err = json.NewDecoder(resp.Body).Decode(v)
if err == io.EOF {
err = nil // ignore EOF errors caused by empty response body
}
} }
} }
return response, err return response, err
@@ -343,8 +361,15 @@ func (r *ErrorResponse) Error() string {
r.Response.StatusCode, r.Message, r.Errors) r.Response.StatusCode, r.Message, r.Errors)
} }
// sanitizeURL redacts the client_id and client_secret tokens from the URL which // TwoFactorAuthError occurs when using HTTP Basic Authentication for a user
// may be exposed to the user, specifically in the ErrorResponse error message. // 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 { func sanitizeURL(uri *url.URL) *url.URL {
if uri == nil { if uri == nil {
return nil return nil
@@ -397,6 +422,9 @@ func CheckResponse(r *http.Response) error {
if err == nil && data != nil { if err == nil && data != nil {
json.Unmarshal(data, errorResponse) json.Unmarshal(data, errorResponse)
} }
if r.StatusCode == http.StatusUnauthorized && strings.HasPrefix(r.Header.Get(headerOTP), "required") {
return (*TwoFactorAuthError)(errorResponse)
}
return errorResponse return errorResponse
} }
@@ -548,6 +576,43 @@ func (t *UnauthenticatedRateLimitedTransport) transport() http.RoundTripper {
return http.DefaultTransport 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 // cloneRequest returns a clone of the provided *http.Request. The clone is a
// shallow copy of the struct and its Header map. // shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request { func cloneRequest(r *http.Request) *http.Request {
@@ -555,9 +620,9 @@ func cloneRequest(r *http.Request) *http.Request {
r2 := new(http.Request) r2 := new(http.Request)
*r2 = *r *r2 = *r
// deep copy of the Header // deep copy of the Header
r2.Header = make(http.Header) r2.Header = make(http.Header, len(r.Header))
for k, s := range r.Header { for k, s := range r.Header {
r2.Header[k] = s r2.Header[k] = append([]string(nil), s...)
} }
return r2 return r2
} }

View File

@@ -65,7 +65,7 @@ type IssueListOptions struct {
Filter string `url:"filter,omitempty"` Filter string `url:"filter,omitempty"`
// State filters issues based on their state. Possible values are: open, // 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"` State string `url:"state,omitempty"`
// Labels filters issues based on their label. // Labels filters issues based on their label.
@@ -76,7 +76,7 @@ type IssueListOptions struct {
Sort string `url:"sort,omitempty"` Sort string `url:"sort,omitempty"`
// Direction in which to sort issues. Possible values are: asc, desc. // Direction in which to sort issues. Possible values are: asc, desc.
// Default is "asc". // Default is "desc".
Direction string `url:"direction,omitempty"` Direction string `url:"direction,omitempty"`
// Since filters issues by time. // Since filters issues by time.
@@ -148,7 +148,7 @@ type IssueListByRepoOptions struct {
Milestone string `url:"milestone,omitempty"` Milestone string `url:"milestone,omitempty"`
// State filters issues based on their state. Possible values are: open, // 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"` State string `url:"state,omitempty"`
// Assignee filters issues based on their assignee. Possible values are a // Assignee filters issues based on their assignee. Possible values are a
@@ -156,10 +156,10 @@ type IssueListByRepoOptions struct {
// any assigned user. // any assigned user.
Assignee string `url:"assignee,omitempty"` Assignee string `url:"assignee,omitempty"`
// Assignee filters issues based on their creator. // Creator filters issues based on their creator.
Creator string `url:"creator,omitempty"` 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"` Mentioned string `url:"mentioned,omitempty"`
// Labels filters issues based on their label. // Labels filters issues based on their label.
@@ -170,7 +170,7 @@ type IssueListByRepoOptions struct {
Sort string `url:"sort,omitempty"` Sort string `url:"sort,omitempty"`
// Direction in which to sort issues. Possible values are: asc, desc. // Direction in which to sort issues. Possible values are: asc, desc.
// Default is "asc". // Default is "desc".
Direction string `url:"direction,omitempty"` Direction string `url:"direction,omitempty"`
// Since filters issues by time. // Since filters issues by time.

View File

@@ -22,40 +22,52 @@ type IssueEvent struct {
// values are: // values are:
// //
// closed // closed
// The issue was closed by the actor. When the commit_id is // The Actor closed the issue.
// present, it identifies the commit that closed the issue using // If the issue was closed by commit message, CommitID holds the SHA1 hash of the commit.
// “closes / fixes #NN” syntax.
//
// reopened
// The issue was reopened by the actor.
//
// subscribed
// The actor subscribed to receive notifications for an issue.
// //
// merged // 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 // 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 // mentioned
// The actor was @mentioned in an issue body. // Someone unspecified @mentioned the Actor [sic] in an issue comment body.
// //
// assigned // assigned, unassigned
// The issue was assigned to the actor. // The Actor assigned the issue to or removed the assignment from the Assignee.
// //
// head_ref_deleted // labeled, unlabeled
// The pull requests branch was deleted. // 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 requests branch was deleted or restored.
// //
// head_ref_restored
// The pull requests branch was restored.
Event *string `json:"event,omitempty"` 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"` CreatedAt *time.Time `json:"created_at,omitempty"`
Issue *Issue `json:"issue,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. // 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 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)
}

View File

@@ -49,7 +49,7 @@ type MilestoneListOptions struct {
// //
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository // 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) { 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) u, err := addOptions(u, opt)
if err != nil { if err != nil {
return nil, nil, err 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 // 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) { 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) req, err := s.client.NewRequest("GET", u, nil)
if err != nil { if err != nil {
return nil, nil, err 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 // 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) { 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) req, err := s.client.NewRequest("POST", u, milestone)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@@ -52,7 +52,7 @@ type ListMembersOptions struct {
// 2fa_disabled, all. Default is "all". // 2fa_disabled, all. Default is "all".
Filter string `url:"filter,omitempty"` 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: // Possible values are:
// all - all members of the organization, regardless of role // all - all members of the organization, regardless of role
// admin - organization owners // admin - organization owners
@@ -172,7 +172,7 @@ func (s *OrganizationsService) ConcealMembership(org, user string) (*Response, e
// ListOrgMembershipsOptions specifies optional parameters to the // ListOrgMembershipsOptions specifies optional parameters to the
// OrganizationsService.ListOrgMemberships method. // OrganizationsService.ListOrgMemberships method.
type ListOrgMembershipsOptions struct { 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". // Possible values are: "active", "pending".
State string `url:"state,omitempty"` 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/#add-or-update-organization-membership
// GitHub API docs: https://developer.github.com/v3/orgs/members/#edit-your-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) { func (s *OrganizationsService) EditOrgMembership(user, org string, membership *Membership) (*Membership, *Response, error) {
var u string var u, method string
if user != "" { if user != "" {
u = fmt.Sprintf("orgs/%v/memberships/%v", org, user) u = fmt.Sprintf("orgs/%v/memberships/%v", org, user)
method = "PUT"
} else { } else {
u = fmt.Sprintf("user/memberships/orgs/%v", org) 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 { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@@ -12,14 +12,17 @@ import (
// PullRequestComment represents a comment left on a pull request. // PullRequestComment represents a comment left on a pull request.
type PullRequestComment struct { type PullRequestComment struct {
ID *int `json:"id,omitempty"` ID *int `json:"id,omitempty"`
Body *string `json:"body,omitempty"` Body *string `json:"body,omitempty"`
Path *string `json:"path,omitempty"` Path *string `json:"path,omitempty"`
Position *int `json:"position,omitempty"` DiffHunk *string `json:"diff_hunk,omitempty"`
CommitID *string `json:"commit_id,omitempty"` Position *int `json:"position,omitempty"`
User *User `json:"user,omitempty"` OriginalPosition *int `json:"original_position,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"` CommitID *string `json:"commit_id,omitempty"`
UpdatedAt *time.Time `json:"updated_at,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 { func (p PullRequestComment) String() string {

View File

@@ -146,6 +146,9 @@ func (s *RepositoriesService) List(user string, opt *RepositoryListOptions) ([]R
return nil, nil, err return nil, nil, err
} }
// TODO: remove custom Accept header when license support fully launches
req.Header.Set("Accept", mediaTypeLicensesPreview)
repos := new([]Repository) repos := new([]Repository)
resp, err := s.client.Do(req, repos) resp, err := s.client.Do(req, repos)
if err != nil { if err != nil {
@@ -180,6 +183,9 @@ func (s *RepositoriesService) ListByOrg(org string, opt *RepositoryListByOrgOpti
return nil, nil, err return nil, nil, err
} }
// TODO: remove custom Accept header when license support fully launches
req.Header.Set("Accept", mediaTypeLicensesPreview)
repos := new([]Repository) repos := new([]Repository)
resp, err := s.client.Do(req, repos) resp, err := s.client.Do(req, repos)
if err != nil { if err != nil {
@@ -442,8 +448,27 @@ func (s *RepositoriesService) ListTags(owner string, repo string, opt *ListOptio
// Branch represents a repository branch // Branch represents a repository branch
type Branch struct { type Branch struct {
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
Commit *Commit `json:"commit,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. // 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 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) b := new(Branch)
resp, err := s.client.Do(req, b) resp, err := s.client.Do(req, b)
if err != nil { if err != nil {

View File

@@ -70,6 +70,7 @@ type Hook struct {
CreatedAt *time.Time `json:"created_at,omitempty"` CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"` UpdatedAt *time.Time `json:"updated_at,omitempty"`
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
URL *string `json:"url,omitempty"`
Events []string `json:"events,omitempty"` Events []string `json:"events,omitempty"`
Active *bool `json:"active,omitempty"` Active *bool `json:"active,omitempty"`
Config map[string]interface{} `json:"config,omitempty"` Config map[string]interface{} `json:"config,omitempty"`

View File

@@ -10,7 +10,6 @@ import (
"fmt" "fmt"
"io" "io"
"mime" "mime"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
) )
@@ -213,9 +212,9 @@ func (s *RepositoriesService) GetReleaseAsset(owner, repo string, id int) (*Rele
return asset, resp, err 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. // 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 // 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) req.Header.Set("Accept", defaultMediaType)
var resp *http.Response resp, err := s.client.client.Do(req)
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())
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -113,7 +113,7 @@ func (s *UsersService) Edit(user *User) (*User, *Response, error) {
return uResp, resp, err return uResp, resp, err
} }
// UserListOptions specifies optional parameters to the UsersService.List // UserListOptions specifies optional parameters to the UsersService.ListAll
// method. // method.
type UserListOptions struct { type UserListOptions struct {
// ID of the last user seen // ID of the last user seen

View File

@@ -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.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.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.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") flagSet.BoolVar(&(DefaultReporterConfig.Succinct), prefix+"succinct", false, "If set, default reporter prints out a very succinct report")

View File

@@ -63,6 +63,8 @@ func findSuiteFile() (string, os.FileMode) {
if err != nil { if err != nil {
complainAndQuit("Could not find suite file for nodot: " + err.Error()) complainAndQuit("Could not find suite file for nodot: " + err.Error())
} }
defer f.Close()
if re.MatchReader(bufio.NewReader(f)) { if re.MatchReader(bufio.NewReader(f)) {
return path, file.Mode() return path, file.Mode()
} }

View File

@@ -4,6 +4,7 @@ import (
"errors" "errors"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
@@ -47,6 +48,15 @@ func PrecompiledTestSuite(path string) (TestSuite, error) {
func SuitesInDir(dir string, recurse bool) []TestSuite { func SuitesInDir(dir string, recurse bool) []TestSuite {
suites := []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) files, _ := ioutil.ReadDir(dir)
re := regexp.MustCompile(`_test\.go$`) re := regexp.MustCompile(`_test\.go$`)
for _, file := range files { for _, file := range files {

View File

@@ -24,6 +24,8 @@ func unfocusSpecs([]string, []string) {
unfocus("Context") unfocus("Context")
unfocus("It") unfocus("It")
unfocus("Measure") unfocus("Measure")
unfocus("DescribeTable")
unfocus("Entry")
} }
func unfocus(component string) { func unfocus(component string) {

View File

@@ -68,7 +68,7 @@ func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure)
done := make(chan interface{}, 1) done := make(chan interface{}, 1)
go func() { go func() {
finished := false finished := false
defer func() { defer func() {
if e := recover(); e != nil || !finished { if e := recover(); e != nil || !finished {
@@ -83,7 +83,7 @@ func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure)
}() }()
r.asyncFunc(done) r.asyncFunc(done)
finished = true finished = true
}() }()
select { select {
@@ -96,7 +96,7 @@ func (r *runner) runAsync() (outcome types.SpecState, failure types.SpecFailure)
return return
} }
func (r *runner) runSync() (outcome types.SpecState, failure types.SpecFailure) { func (r *runner) runSync() (outcome types.SpecState, failure types.SpecFailure) {
finished := false finished := false
defer func() { defer func() {
if e := recover(); e != nil || !finished { if e := recover(); e != nil || !finished {
@@ -107,7 +107,7 @@ func (r *runner) runSync() (outcome types.SpecState, failure types.SpecFailure)
}() }()
r.syncFunc() r.syncFunc()
finished = true finished = true
return return
} }

View File

@@ -8,6 +8,7 @@ package stenographer
import ( import (
"fmt" "fmt"
"runtime"
"strings" "strings"
"github.com/onsi/ginkgo/types" "github.com/onsi/ginkgo/types"
@@ -59,14 +60,20 @@ type Stenographer interface {
} }
func New(color bool) Stenographer { func New(color bool) Stenographer {
denoter := "•"
if runtime.GOOS == "windows" {
denoter = "+"
}
return &consoleStenographer{ return &consoleStenographer{
color: color, color: color,
denoter: denoter,
cursorState: cursorStateTop, cursorState: cursorStateTop,
} }
} }
type consoleStenographer struct { type consoleStenographer struct {
color bool color bool
denoter string
cursorState cursorStateType cursorState cursorStateType
} }
@@ -216,13 +223,13 @@ func (s *consoleStenographer) AnnounceCapturedOutput(output string) {
} }
func (s *consoleStenographer) AnnounceSuccesfulSpec(spec *types.SpecSummary) { func (s *consoleStenographer) AnnounceSuccesfulSpec(spec *types.SpecSummary) {
s.print(0, s.colorize(greenColor, "•")) s.print(0, s.colorize(greenColor, s.denoter))
s.stream() s.stream()
} }
func (s *consoleStenographer) AnnounceSuccesfulSlowSpec(spec *types.SpecSummary, succinct bool) { func (s *consoleStenographer) AnnounceSuccesfulSlowSpec(spec *types.SpecSummary, succinct bool) {
s.printBlockWithMessage( 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, spec,
succinct, succinct,
@@ -231,7 +238,7 @@ func (s *consoleStenographer) AnnounceSuccesfulSlowSpec(spec *types.SpecSummary,
func (s *consoleStenographer) AnnounceSuccesfulMeasurement(spec *types.SpecSummary, succinct bool) { func (s *consoleStenographer) AnnounceSuccesfulMeasurement(spec *types.SpecSummary, succinct bool) {
s.printBlockWithMessage( s.printBlockWithMessage(
s.colorize(greenColor, " [MEASUREMENT]"), s.colorize(greenColor, "%s [MEASUREMENT]", s.denoter),
s.measurementReport(spec, succinct), s.measurementReport(spec, succinct),
spec, spec,
succinct, succinct,
@@ -270,15 +277,15 @@ func (s *consoleStenographer) AnnounceSkippedSpec(spec *types.SpecSummary, succi
} }
func (s *consoleStenographer) AnnounceSpecTimedOut(spec *types.SpecSummary, succinct bool, fullTrace bool) { 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) { 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) { 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) { func (s *consoleStenographer) SummarizeFailures(summaries []*types.SpecSummary) {

View File

@@ -1,3 +1,5 @@
.DS_Store .DS_Store
*.test *.test
. .
.idea
gomega.iml

View File

@@ -10,15 +10,18 @@ type HaveOccurredMatcher struct {
} }
func (matcher *HaveOccurredMatcher) Match(actual interface{}) (success bool, err error) { func (matcher *HaveOccurredMatcher) Match(actual interface{}) (success bool, err error) {
if isNil(actual) { // is purely nil?
if actual == nil {
return false, nil return false, nil
} }
if isError(actual) { // must be an 'error' type
return true, nil 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) { func (matcher *HaveOccurredMatcher) FailureMessage(actual interface{}) (message string) {

View File

@@ -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)) return false, fmt.Errorf("HaveSuffix matcher requires a string or stringer. Got:\n%s", format.Object(actual, 1))
} }
suffix := matcher.suffix() 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 { func (matcher *HaveSuffixMatcher) suffix() string {

View File

@@ -10,15 +10,18 @@ type SucceedMatcher struct {
} }
func (matcher *SucceedMatcher) Match(actual interface{}) (success bool, err error) { func (matcher *SucceedMatcher) Match(actual interface{}) (success bool, err error) {
// is purely nil?
if actual == nil { if actual == nil {
return true, nil return true, nil
} }
if isError(actual) { // must be an 'error' type
return false, nil 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) { func (matcher *SucceedMatcher) FailureMessage(actual interface{}) (message string) {

View File

@@ -3,5 +3,5 @@ package util
import "math" import "math"
func Odd(n int) bool { func Odd(n int) bool {
return math.Mod(float64(n), 2.0) == 1.0 return math.Mod(float64(n), 2.0) == 1.0
} }

View File

@@ -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 Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are modification, are permitted provided that the following conditions are

View File

@@ -1,7 +1,7 @@
Additional IP Rights Grant (Patents) Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by "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, Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section) no-charge, royalty-free, irrevocable (except as stated in this section)

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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 = &notifyingReader{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
}

14
Godeps/_workspace/src/golang.org/x/oauth2/.travis.yml generated vendored Normal file
View File

@@ -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/...

3
Godeps/_workspace/src/golang.org/x/oauth2/AUTHORS generated vendored Normal file
View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

27
Godeps/_workspace/src/golang.org/x/oauth2/LICENSE generated vendored Normal file
View File

@@ -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.

64
Godeps/_workspace/src/golang.org/x/oauth2/README.md generated vendored Normal file
View File

@@ -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("...")
}

View File

@@ -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",
}

View File

@@ -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
}

View File

@@ -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, " ")),
})
}

View File

@@ -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",
}

View File

@@ -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",
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)
}
}

Some files were not shown because too many files have changed in this diff Show More