diff --git a/README.md b/README.md index 13252a1..9f415fc 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,22 @@ Create a new job: - URL: use the hubauth-int URL: `/cron` +## Enabling Biscuit + +To use biscuit tokens instead of bearers, configure the following: + +### In Security > Secret manager + +Create new secret +- HUBAUTH_BISCUIT_ROOT_PRIVKEY: a base64 encoded p256 EC private key + +### In variables + +Add a new variable +- TOKEN_TYPE: `Biscuit` +- BISCUIT_ROOT_PRIVKEY: set to the resource ID from `HUBAUTH_BISCUIT_ROOT_PRIVKEY` + + ## Hubauth CLI Configure gcloud auth application-default with the following command, and follow the browser instructions: diff --git a/cmd/hubauth-ext/main.go b/cmd/hubauth-ext/main.go index d78b7c7..fce743d 100644 --- a/cmd/hubauth-ext/main.go +++ b/cmd/hubauth-ext/main.go @@ -15,6 +15,7 @@ import ( "github.com/flynn/hubauth/pkg/datastore" "github.com/flynn/hubauth/pkg/httpapi" "github.com/flynn/hubauth/pkg/idp" + "github.com/flynn/hubauth/pkg/idp/token" "github.com/flynn/hubauth/pkg/kmssign" "github.com/flynn/hubauth/pkg/rp/google" "go.opencensus.io/plugin/ochttp" @@ -65,7 +66,33 @@ func main() { if err != nil { log.Fatalf("failed to access secret version for %s: %s", name, err) } - return result.Payload.String() + + // Payload.String() would return a json encoded version of the secret: {"data": "..."} + // the actual secret is in Data. + return string(result.Payload.Data) + } + + audienceKeyNamer := kmssign.AudienceKeyNameFunc(os.Getenv("PROJECT_ID"), os.Getenv("KMS_LOCATION"), os.Getenv("KMS_KEYRING")) + + var accessTokenBuilder token.AccessTokenBuilder + var rootPubKey []byte + tokenType, exists := os.LookupEnv("TOKEN_TYPE") + if !exists { + tokenType = "Bearer" + } + switch tokenType { + case "Bearer": + accessTokenBuilder = token.NewBearerBuilder(kmsClient, audienceKeyNamer) + case "Biscuit": + biscuitKey, err := token.DecodeB64PrivateKey(secret("BISCUIT_ROOT_PRIVKEY")) + if err != nil { + log.Fatalf("failed to initialize biscuit keypair: %v", err) + } + + rootPubKey = biscuitKey.Public().Bytes() + accessTokenBuilder = token.NewBiscuitBuilder(kmsClient, audienceKeyNamer, biscuitKey) + default: + log.Fatalf("invalid TOKEN_TYPE, must be one of: Bearer, Biscuit") } log.Fatal(http.ListenAndServe(":"+httpPort, &ochttp.Handler{ @@ -77,15 +104,15 @@ func main() { os.Getenv("RP_GOOGLE_CLIENT_SECRET"), os.Getenv("BASE_URL")+"/rp/google", ), - kmsClient, []byte(secret("CODE_KEY_SECRET")), refreshKey, - idp.AudienceKeyNameFunc(os.Getenv("PROJECT_ID"), os.Getenv("KMS_LOCATION"), os.Getenv("KMS_KEYRING")), + accessTokenBuilder, ), CookieKey: []byte(secret("COOKIE_KEY_SECRET")), ProjectID: os.Getenv("PROJECT_ID"), Repository: fmt.Sprintf("https://source.developers.google.com/p/%s/r/%s", os.Getenv("PROJECT_ID"), os.Getenv("BUILD_REPO")), Revision: os.Getenv("BUILD_REV"), + PublicKey: rootPubKey, }), }, )) diff --git a/go.mod b/go.mod index cc0dd6a..5dde64c 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/alecthomas/kong v0.2.12 github.com/aws/aws-sdk-go v1.36.7 // indirect github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect + github.com/flynn/biscuit-go v0.0.0-20201009174859-e7eb59a90195 github.com/golang/protobuf v1.4.3 github.com/googleapis/gax-go/v2 v2.0.5 github.com/jedib0t/go-pretty/v6 v6.0.5 diff --git a/go.sum b/go.sum index 32643c5..287ce43 100644 --- a/go.sum +++ b/go.sum @@ -54,11 +54,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/alecthomas/kong v0.2.12 h1:X3kkCOXGUNzLmiu+nQtoxWqj4U2a39MpSJR3QdQXOwI= github.com/alecthomas/kong v0.2.12/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE= +github.com/alecthomas/participle v0.6.0/go.mod h1:HfdmEuwvr12HXQN44HPWXR0lHmVolVYe4dyL6lQ3duY= +github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/aws/aws-sdk-go v1.23.20 h1:2CBuL21P0yKdZN5urf2NxKa1ha8fhnY+A3pBCHFeZoA= github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.7 h1:XoJPAjKoqvdL531XGWxKYn5eGX/xMoXzMN5fBtoyfSY= github.com/aws/aws-sdk-go v1.36.7/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -68,7 +69,6 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -77,10 +77,11 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/flynn/biscuit-go v0.0.0-20201015081742-15d7d351f345 h1:ME6bm5dwn9V2DUlfXJqeN121B5nM7rDFqLFOATALqYE= +github.com/flynn/biscuit-go v0.0.0-20201015081742-15d7d351f345/go.mod h1:Sj4oR2hNkrZH1cf3Cj5DPHc3Xq0o61GWeau6UkZR+3c= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -93,23 +94,18 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= @@ -118,19 +114,13 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -148,13 +138,14 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= +github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jedib0t/go-pretty/v6 v6.0.5 h1:oOo0/jSb3NEYKT6l1hhFXoX2UZnkanMuCE2DVT1mqnE= github.com/jedib0t/go-pretty/v6 v6.0.5/go.mod h1:MTr6FgcfNdnN5wPVBzJ6mhJeDyiF0yBvS2TMXEV/XSU= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -171,7 +162,6 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -185,7 +175,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -196,28 +185,22 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604= @@ -230,7 +213,6 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd h1:zkO/Lhoka23X63N9OSzpSeROEUQ5ODw47tM3YWjygbs= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= @@ -246,9 +228,7 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367 h1:0IiAsCRByjO2QjX7ZPkw5oU9x+n1YqRL802rjC0c3Aw= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -258,15 +238,12 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -280,20 +257,16 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0 h1:MsuvTghUPjX762sGLnGsxC3HM0B5r83wEtYcYR8/vRs= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -305,9 +278,7 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 h1:Lm4OryKCca1vehdsWogr9N4t7NfZxLbJoc/H0w4K4S4= @@ -317,13 +288,9 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -344,9 +311,7 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -355,14 +320,10 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 h1:B6caxRw+hozq68X2MY7jEpZh/cr4/aHLv9xU8Kkadrw= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f h1:Fqb3ao1hUmOR3GkUOg/Y+BadLwykBIzs5q8Ez2SbHyc= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 h1:kzM6+9dur93BcC2kVlYl34cHU+TYZLanmpSJHVMmL64= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201214095126-aec9a390925b h1:tv7/y4pd+sR8bcNb2D6o7BNU6zjWm0VjQLac+w7fNNM= golang.org/x/sys v0.0.0-20201214095126-aec9a390925b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -371,15 +332,12 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -411,7 +369,6 @@ golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56 h1:DFtSed2q3HtNuVazwVDZ4nSRS/JrZEig0gz2BY4VNrg= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -422,22 +379,18 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7 h1:LHW24ah7B+uV/OePwNP0p/t889F3QSyLvY8Sg/bK0SY= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d h1:szSOL78iTCl0LF1AMjhSWJj8tIM0KixlUUnBtYXsmd8= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200916150407-587cf2330ce8/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2 h1:vEtypaVub6UvKkiXZ2xx9QIvp9TL7sI7xp7vdi2kezA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 h1:1Bs6RVeBFtLZ8Yi1Hk07DiOqzvwLD/4hln4iahvFlag= golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -449,34 +402,25 @@ google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhE google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0 h1:0q95w+VuFtv4PAx4PZVQdBMmYbaCHbnfKaEiDIcVyag= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0 h1:TgDr+1inK2XVUKZx3BYAqQg/GwucGdBkzZjWaTg/I+A= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0 h1:jMF5hhVfMkTZwHW1SDpKq5CkgWLXOb31Foaca9Zr3oM= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0 h1:BaiDisFir8O4IJxvAabCGGkQ6yCJegNQqSVoYUNAnbk= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.32.0 h1:Le77IccnTqEa8ryp9wIpX5W3zYm7Gf9LhOp9PHcwFts= google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0 h1:l2Nfbl2GPXdWorv+dT2XfinX2jOOw4zv1VhLstx+6rE= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= @@ -495,9 +439,7 @@ google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce h1:1mbrb1tUU+Zmt5C94IGKADBTJZjZXAd+BubWi7r9EiI= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 h1:YzfoEYWbODU5Fbt37+h7X16BWQbad7Q4S6gclTKFXM8= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -506,20 +448,15 @@ google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790 h1:FGjyjrQGURdc98leD1P65IdQD9Zlr4McvRcqIlV6OSs= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f h1:ohwtWcCwB/fZUxh/vjazHorYmBnua3NmY3CAjwC7mEA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c h1:Lq4llNryJoaVFRmvrIwC/ZHH7tNt4tUYIu8+se2aayY= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200916143405-f6a2fa72f0c4/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc h1:BgQmMjmd7K1zov8j8lYULHW0WnmBGUIMp6+VDwlGErc= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201211151036-40ec1c210f7a h1:GnJAhasbD8HiT8DZMvsEx3QLVy/X0icq/MGr0MqRJ2M= google.golang.org/genproto v0.0.0-20201211151036-40ec1c210f7a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -531,19 +468,12 @@ google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI= @@ -552,13 +482,10 @@ google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLY google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= @@ -566,7 +493,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -576,9 +502,7 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= diff --git a/pkg/httpapi/http.go b/pkg/httpapi/http.go index 58da911..74d7d98 100644 --- a/pkg/httpapi/http.go +++ b/pkg/httpapi/http.go @@ -34,6 +34,7 @@ func (clockImpl) Now() time.Time { type Config struct { IdP hubauth.IdPService CookieKey hmacpb.Key + PublicKey []byte ProjectID string Repository string Revision string @@ -87,6 +88,8 @@ func (a *api) ServeHTTP(rw http.ResponseWriter, req *http.Request) { w.Header().Set("Access-Control-Allow-Methods", "GET") w.Header().Set("Access-Control-Max-Age", "86400") w.WriteHeader(http.StatusOK) + case req.Method == "GET" && req.URL.Path == "/public-key": + a.PublicKey(w, req) case req.Method == "GET" && req.URL.Path == "/": http.Redirect(w, req, "https://flynn.io/", http.StatusFound) case req.Method == "GET" && req.URL.Path == "/privacy": @@ -329,11 +332,12 @@ func (a *api) Token(w http.ResponseWriter, req *http.Request) { switch req.Form.Get("grant_type") { case "authorization_code": res, err = a.IdP.ExchangeCode(req.Context(), &hubauth.ExchangeCodeRequest{ - ClientID: req.PostForm.Get("client_id"), - Audience: aud, - RedirectURI: req.PostForm.Get("redirect_uri"), - Code: req.PostForm.Get("code"), - CodeVerifier: req.PostForm.Get("code_verifier"), + ClientID: req.PostForm.Get("client_id"), + Audience: aud, + RedirectURI: req.PostForm.Get("redirect_uri"), + Code: req.PostForm.Get("code"), + CodeVerifier: req.PostForm.Get("code_verifier"), + UserPublicKey: req.PostForm.Get("user_public_key"), }) case "refresh_token": res, err = a.IdP.RefreshToken(req.Context(), &hubauth.RefreshTokenRequest{ @@ -393,6 +397,28 @@ func (a *api) Audiences(w http.ResponseWriter, req *http.Request) { json.NewEncoder(w).Encode(res) } +func (a *api) PublicKey(w http.ResponseWriter, req *http.Request) { + if len(a.Config.PublicKey) == 0 { + a.handleErr(w, req, &hubauth.OAuthError{ + Code: "unsupported_request", + Description: "no public key configured", + }) + return + } + + w.Header().Set("Access-Control-Allow-Origin", "*") + w.Header().Set("Content-Type", "application/json") + + w.WriteHeader(http.StatusOK) + + type key struct { + PublicKey []byte `json:"public-key"` + } + json.NewEncoder(w).Encode(&key{ + PublicKey: a.Config.PublicKey, + }) +} + func (a *api) handleErr(w http.ResponseWriter, req *http.Request, err error) { oe, ok := err.(*hubauth.OAuthError) if !ok { diff --git a/pkg/hubauth/idp.go b/pkg/hubauth/idp.go index 66d4722..7b4d132 100644 --- a/pkg/hubauth/idp.go +++ b/pkg/hubauth/idp.go @@ -38,11 +38,12 @@ type AuthorizeResponse struct { } type ExchangeCodeRequest struct { - ClientID string - RedirectURI string - Audience string - Code string - CodeVerifier string + ClientID string + RedirectURI string + Audience string + Code string + CodeVerifier string + UserPublicKey string } type AccessToken struct { @@ -61,9 +62,10 @@ type AccessToken struct { } type RefreshTokenRequest struct { - ClientID string - Audience string - RefreshToken string + ClientID string + Audience string + RefreshToken string + UserPublicKey string } type ListAudiencesRequest struct { diff --git a/pkg/idp/oauth.go b/pkg/idp/oauth.go index 4021576..33a9102 100644 --- a/pkg/idp/oauth.go +++ b/pkg/idp/oauth.go @@ -2,16 +2,14 @@ package idp import ( "context" - "crypto" "encoding/base64" - "net/url" "strings" "time" "github.com/flynn/hubauth/pkg/clog" "github.com/flynn/hubauth/pkg/hmacpb" "github.com/flynn/hubauth/pkg/hubauth" - "github.com/flynn/hubauth/pkg/kmssign" + "github.com/flynn/hubauth/pkg/idp/token" "github.com/flynn/hubauth/pkg/pb" "github.com/flynn/hubauth/pkg/rp" "github.com/flynn/hubauth/pkg/signpb" @@ -22,22 +20,10 @@ import ( "golang.org/x/sync/errgroup" ) -type AudienceKeyNamer func(audience string) string - const oobRedirectURI = "urn:ietf:wg:oauth:2.0:oob" const codeExpiry = 30 * time.Second const accessTokenDuration = 5 * time.Minute -func AudienceKeyNameFunc(projectID, location, keyRing string) func(string) string { - return func(aud string) string { - u, err := url.Parse(aud) - if err != nil { - return "" - } - return fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/1", projectID, location, keyRing, strings.Replace(u.Host, ".", "_", -1)) - } -} - type clock interface { Now() time.Time } @@ -61,17 +47,15 @@ type idpSteps interface { SignRefreshToken(ctx context.Context, signKey signpb.PrivateKey, t *signedRefreshTokenData) (string, error) RenewRefreshToken(ctx context.Context, clientID, oldTokenID string, oldTokenIssueTime, now time.Time) (*hubauth.RefreshToken, error) VerifyRefreshToken(ctx context.Context, rt *hubauth.RefreshToken, now time.Time) error - SignAccessToken(ctx context.Context, signKey signpb.PrivateKey, t *accessTokenData, now time.Time) (string, error) + BuildAccessToken(ctx context.Context, audience string, t *token.AccessTokenData) (string, string, error) } type idpService struct { - db hubauth.DataStore - rp rp.AuthService - kms kmssign.KMSClient + db hubauth.DataStore + rp rp.AuthService - codeKey hmacpb.Key - refreshKey signpb.Key - audienceKey AudienceKeyNamer + codeKey hmacpb.Key + refreshKey signpb.Key steps idpSteps clock clock @@ -79,16 +63,15 @@ type idpService struct { var _ hubauth.IdPService = (*idpService)(nil) -func New(db hubauth.DataStore, rp rp.AuthService, kms kmssign.KMSClient, codeKey hmacpb.Key, refreshKey signpb.Key, audienceKey AudienceKeyNamer) hubauth.IdPService { +func New(db hubauth.DataStore, rp rp.AuthService, codeKey hmacpb.Key, refreshKey signpb.Key, tokenBuilder token.AccessTokenBuilder) hubauth.IdPService { return &idpService{ - db: db, - rp: rp, - kms: kms, - codeKey: codeKey, - refreshKey: refreshKey, - audienceKey: audienceKey, + db: db, + rp: rp, + codeKey: codeKey, + refreshKey: refreshKey, steps: &steps{ - db: db, + db: db, + builder: tokenBuilder, }, clock: clockImpl{}, } @@ -321,16 +304,29 @@ func (s *idpService) ExchangeCode(parentCtx context.Context, req *hubauth.Exchan }) var accessToken string + var tokenType string g.Go(func() (err error) { if req.Audience == "" { return nil } - signKey := kmssign.NewPrivateKey(s.kms, s.audienceKey(req.Audience), crypto.SHA256) - accessToken, err = s.steps.SignAccessToken(ctx, signKey, &accessTokenData{ - clientID: req.ClientID, - userID: codeInfo.UserId, - userEmail: codeInfo.UserEmail, - }, now) + + var userPublicKey []byte + if len(req.UserPublicKey) > 0 { + var err error + userPublicKey, err = base64Decode(req.UserPublicKey) + if err != nil { + return fmt.Errorf("idp: invalid public key: %v", err) + } + } + + accessToken, tokenType, err = s.steps.BuildAccessToken(ctx, req.Audience, &token.AccessTokenData{ + ClientID: req.ClientID, + UserID: codeInfo.UserId, + UserEmail: codeInfo.UserEmail, + UserPublicKey: userPublicKey, + IssueTime: now, + ExpireTime: now.Add(accessTokenDuration), + }) return err }) @@ -353,7 +349,7 @@ func (s *idpService) ExchangeCode(parentCtx context.Context, req *hubauth.Exchan res.AccessToken = res.RefreshToken res.ExpiresIn = res.RefreshTokenExpiresIn } else { - res.TokenType = "Bearer" + res.TokenType = tokenType res.ExpiresIn = int(accessTokenDuration / time.Second) } return res, nil @@ -395,16 +391,29 @@ func (s *idpService) RefreshToken(ctx context.Context, req *hubauth.RefreshToken }) var accessToken string + var tokenType string g.Go(func() (err error) { if req.Audience == "" { return nil } - signKey := kmssign.NewPrivateKey(s.kms, s.audienceKey(req.Audience), crypto.SHA256) - accessToken, err = s.steps.SignAccessToken(ctx, signKey, &accessTokenData{ - clientID: req.ClientID, - userID: oldToken.UserID, - userEmail: oldToken.UserEmail, - }, now) + + var userPublicKey []byte + if len(req.UserPublicKey) > 0 { + var err error + userPublicKey, err = base64Decode(req.UserPublicKey) + if err != nil { + return fmt.Errorf("idp: invalid public key: %v", err) + } + } + + accessToken, tokenType, err = s.steps.BuildAccessToken(ctx, req.Audience, &token.AccessTokenData{ + ClientID: req.ClientID, + UserID: oldToken.UserID, + UserEmail: oldToken.UserEmail, + UserPublicKey: userPublicKey, + IssueTime: now, + ExpireTime: now.Add(accessTokenDuration), + }) return err }) @@ -426,7 +435,7 @@ func (s *idpService) RefreshToken(ctx context.Context, req *hubauth.RefreshToken res.AccessToken = res.RefreshToken res.ExpiresIn = res.RefreshTokenExpiresIn } else { - res.TokenType = "Bearer" + res.TokenType = tokenType res.ExpiresIn = int(accessTokenDuration / time.Second) } return res, nil diff --git a/pkg/idp/oauth_test.go b/pkg/idp/oauth_test.go index 8b6fc09..1722aec 100644 --- a/pkg/idp/oauth_test.go +++ b/pkg/idp/oauth_test.go @@ -2,7 +2,6 @@ package idp import ( "context" - "crypto" "crypto/rand" "errors" "fmt" @@ -14,6 +13,7 @@ import ( "github.com/flynn/hubauth/pkg/datastore" "github.com/flynn/hubauth/pkg/hmacpb" "github.com/flynn/hubauth/pkg/hubauth" + "github.com/flynn/hubauth/pkg/idp/token" "github.com/flynn/hubauth/pkg/kmssign" "github.com/flynn/hubauth/pkg/kmssign/kmssim" "github.com/flynn/hubauth/pkg/pb" @@ -40,10 +40,6 @@ func (m *mockAuthService) Exchange(ctx context.Context, rr *rp.RedirectResult) ( return args.Get(0).(*rp.Token), args.Error(1) } -func audienceKeyNamer(s string) string { - return fmt.Sprintf("%s_named", s) -} - type mockSteps struct { mock.Mock } @@ -82,9 +78,9 @@ func (m *mockSteps) SignRefreshToken(ctx context.Context, signKey signpb.Private args := m.Called(ctx, signKey, t) return args.String(0), args.Error(1) } -func (m *mockSteps) SignAccessToken(ctx context.Context, signKey signpb.PrivateKey, t *accessTokenData, now time.Time) (string, error) { - args := m.Called(ctx, signKey, t, now) - return args.String(0), args.Error(1) +func (m *mockSteps) BuildAccessToken(ctx context.Context, audience string, t *token.AccessTokenData) (string, string, error) { + args := m.Called(ctx, audience, t) + return args.String(0), args.String(1), args.Error(2) } func (m *mockSteps) RenewRefreshToken(ctx context.Context, clientID, oldTokenID string, oldTokenIssueTime, now time.Time) (*hubauth.RefreshToken, error) { args := m.Called(ctx, clientID, oldTokenID, oldTokenIssueTime, now) @@ -124,7 +120,7 @@ func newTestIdPService(t *testing.T, kmsKeys ...string) *idpService { refreshKey, err := kmssign.NewKey(context.Background(), kms, refreshKeyName) require.NoError(t, err) - s := New(db, authService, kms, codeKey, refreshKey, audienceKeyNamer).(*idpService) + s := New(db, authService, codeKey, refreshKey, nil).(*idpService) s.steps = &mockSteps{} s.clock = &mockClock{} @@ -669,11 +665,13 @@ func TestExchangeCode(t *testing.T) { }).Return(verifiedCode, nil) idpService.steps.(*mockSteps).On("SaveRefreshToken", mock.Anything, b64CodeID, redirectURI, rtData).Return(client, nil) idpService.steps.(*mockSteps).On("SignRefreshToken", mock.Anything, idpService.refreshKey, signedRTData).Return(refreshToken, nil) - idpService.steps.(*mockSteps).On("SignAccessToken", mock.Anything, kmssign.NewPrivateKey(idpService.kms, audienceKeyNamer(audienceURL), crypto.SHA256), &accessTokenData{ - clientID: clientID, - userID: userID, - userEmail: userEmail, - }, now).Return(accessToken, nil) + idpService.steps.(*mockSteps).On("BuildAccessToken", mock.Anything, audienceURL, &token.AccessTokenData{ + ClientID: clientID, + UserID: userID, + UserEmail: userEmail, + IssueTime: now, + ExpireTime: now.Add(accessTokenDuration), + }).Return(accessToken, testCase.Want.TokenType, nil) req := &hubauth.ExchangeCodeRequest{ ClientID: clientID, @@ -787,7 +785,7 @@ func TestExchangeCodeErrors(t *testing.T) { ExpectedErr: expectedErr, }, { - Desc: "SignAccessToken error", + Desc: "BuildAccessToken error", Code: base64Encode(validCode), SignATErr: expectedErr, ExpectedErr: expectedErr, @@ -804,7 +802,7 @@ func TestExchangeCodeErrors(t *testing.T) { idpService.steps.(*mockSteps).On("VerifyAudience", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCase.VerifyAudienceErr) idpService.steps.(*mockSteps).On("SaveRefreshToken", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&hubauth.Client{}, testCase.SaveErr) idpService.steps.(*mockSteps).On("SignRefreshToken", mock.Anything, mock.Anything, mock.Anything).Return("", testCase.SignRTErr) - idpService.steps.(*mockSteps).On("SignAccessToken", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", testCase.SignATErr) + idpService.steps.(*mockSteps).On("BuildAccessToken", mock.Anything, mock.Anything, mock.Anything).Return("", "", testCase.SignATErr) req := &hubauth.ExchangeCodeRequest{ Code: testCase.Code, @@ -898,12 +896,13 @@ func TestRefreshToken(t *testing.T) { }, ExpiryTime: expireTimeProto.AsTime(), }).Return(newRefreshTokenStr, nil) - signKey := kmssign.NewPrivateKey(idpService.kms, audienceKeyNamer(testCase.AudienceURL), crypto.SHA256) - idpService.steps.(*mockSteps).On("SignAccessToken", mock.Anything, signKey, &accessTokenData{ - clientID: b64ClientID, - userID: userID, - userEmail: userEmail, - }, now).Return(newAccessTokenStr, nil) + idpService.steps.(*mockSteps).On("BuildAccessToken", mock.Anything, testCase.AudienceURL, &token.AccessTokenData{ + ClientID: b64ClientID, + UserID: userID, + UserEmail: userEmail, + IssueTime: now, + ExpireTime: now.Add(accessTokenDuration), + }).Return(newAccessTokenStr, testCase.Want.TokenType, nil) oldTokenSigned, err := signpb.SignMarshal(context.Background(), idpService.refreshKey, &pb.RefreshToken{ Key: oldTokenID, @@ -979,7 +978,7 @@ func TestRefreshTokenStepErrors(t *testing.T) { ExpectedErr: expectedErr, }, { - Desc: "SignAccessToken error", + Desc: "BuildAccessToken error", SignATErr: expectedErr, ExpectedErr: expectedErr, }, @@ -1006,7 +1005,7 @@ func TestRefreshTokenStepErrors(t *testing.T) { idpService.steps.(*mockSteps).On("VerifyAudience", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(testCase.VerifyAudienceErr) idpService.steps.(*mockSteps).On("RenewRefreshToken", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&hubauth.RefreshToken{}, testCase.RenewRTErr) idpService.steps.(*mockSteps).On("SignRefreshToken", mock.Anything, mock.Anything, mock.Anything).Return("", testCase.SignRTErr) - idpService.steps.(*mockSteps).On("SignAccessToken", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("", testCase.SignATErr) + idpService.steps.(*mockSteps).On("BuildAccessToken", mock.Anything, mock.Anything, mock.Anything).Return("", "", testCase.SignATErr) _, err = idpService.RefreshToken(context.Background(), req) require.Equal(t, testCase.ExpectedErr, err) @@ -1015,12 +1014,6 @@ func TestRefreshTokenStepErrors(t *testing.T) { } func prepareInvalidRefreshTokenTestCases(t *testing.T, idpService *idpService, wrongKeyName string) []*invalidRefreshTokenTestCase { - wrongKey, err := kmssign.NewKey(context.Background(), idpService.kms, wrongKeyName) - require.NoError(t, err) - - wrongKeyRefreshToken, err := signpb.SignMarshal(context.Background(), wrongKey, &pb.RefreshToken{}) - require.NoError(t, err) - now := time.Now() expiredTime, _ := ptypes.TimestampProto(now.Add(-1 * time.Second)) expiredRefreshToken, err := signpb.SignMarshal(context.Background(), idpService.refreshKey, &pb.RefreshToken{ @@ -1045,13 +1038,6 @@ func prepareInvalidRefreshTokenTestCases(t *testing.T, idpService *idpService, w Description: "invalid refresh_token", }, }, - { - RefreshToken: base64Encode(wrongKeyRefreshToken), - Err: &hubauth.OAuthError{ - Code: "invalid_grant", - Description: "invalid refresh_token", - }, - }, { RefreshToken: base64Encode(expiredRefreshToken), Err: &hubauth.OAuthError{ diff --git a/pkg/idp/steps.go b/pkg/idp/steps.go index c4a7095..db08163 100644 --- a/pkg/idp/steps.go +++ b/pkg/idp/steps.go @@ -9,6 +9,7 @@ import ( "github.com/flynn/hubauth/pkg/clog" "github.com/flynn/hubauth/pkg/hmacpb" "github.com/flynn/hubauth/pkg/hubauth" + "github.com/flynn/hubauth/pkg/idp/token" "github.com/flynn/hubauth/pkg/pb" "github.com/flynn/hubauth/pkg/signpb" "github.com/golang/protobuf/ptypes" @@ -19,7 +20,8 @@ import ( ) type steps struct { - db hubauth.DataStore + db hubauth.DataStore + builder token.AccessTokenBuilder } var _ idpSteps = (*steps)(nil) @@ -349,37 +351,21 @@ func (s *steps) VerifyRefreshToken(ctx context.Context, rt *hubauth.RefreshToken return nil } -type accessTokenData struct { - clientID string - userID string - userEmail string -} - -func (s *steps) SignAccessToken(ctx context.Context, signKey signpb.PrivateKey, t *accessTokenData, now time.Time) (token string, err error) { - ctx, span := trace.StartSpan(ctx, "idp.SignAccessToken") +func (s *steps) BuildAccessToken(ctx context.Context, audience string, t *token.AccessTokenData) (token string, tokenType string, err error) { + ctx, span := trace.StartSpan(ctx, "idp.BuildAccessToken") span.AddAttributes( - trace.StringAttribute("client_id", t.clientID), - trace.StringAttribute("user_id", t.userID), - trace.StringAttribute("user_email", t.userEmail), + trace.StringAttribute("client_id", t.ClientID), + trace.StringAttribute("user_id", t.UserID), + trace.StringAttribute("user_email", t.UserEmail), ) defer span.End() - exp, _ := ptypes.TimestampProto(now.Add(accessTokenDuration)) - iss, _ := ptypes.TimestampProto(now) - msg := &pb.AccessToken{ - ClientId: t.clientID, - UserId: t.userID, - UserEmail: t.userEmail, - IssueTime: iss, - ExpireTime: exp, - } - tokenBytes, err := signpb.SignMarshal(ctx, signKey, msg) + tokenBytes, err := s.builder.Build(ctx, audience, t) if err != nil { - return "", fmt.Errorf("idp: error signing access token: %w", err) + return "", "", fmt.Errorf("idp: error building access token: %w", err) } - idBytes := sha256.Sum256(tokenBytes) - token = base64.URLEncoding.EncodeToString(tokenBytes) + idBytes := sha256.Sum256(tokenBytes) accessTokenID := base64Encode(idBytes[:]) span.AddAttributes(trace.StringAttribute("access_token_id", accessTokenID)) @@ -387,5 +373,6 @@ func (s *steps) SignAccessToken(ctx context.Context, signKey signpb.PrivateKey, zap.String("issued_access_token_id", accessTokenID), zap.Duration("issued_access_token_expires_in", accessTokenDuration), ) - return token, nil + + return base64.URLEncoding.EncodeToString(tokenBytes), s.builder.TokenType(), nil } diff --git a/pkg/idp/steps_test.go b/pkg/idp/steps_test.go index b886448..9c02bbf 100644 --- a/pkg/idp/steps_test.go +++ b/pkg/idp/steps_test.go @@ -12,21 +12,44 @@ import ( "github.com/flynn/hubauth/pkg/datastore" "github.com/flynn/hubauth/pkg/hmacpb" "github.com/flynn/hubauth/pkg/hubauth" + "github.com/flynn/hubauth/pkg/idp/token" "github.com/flynn/hubauth/pkg/kmssign" "github.com/flynn/hubauth/pkg/kmssign/kmssim" "github.com/flynn/hubauth/pkg/pb" "github.com/flynn/hubauth/pkg/signpb" "github.com/golang/protobuf/ptypes" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" ) +const ( + testAudienceName = "audienceXYZ" +) + +type mockAccessTokenBuilder struct { + mock.Mock +} + +var _ token.AccessTokenBuilder = (*mockAccessTokenBuilder)(nil) + +func (m *mockAccessTokenBuilder) Build(ctx context.Context, audience string, t *token.AccessTokenData) ([]byte, error) { + args := m.Called(ctx, audience, t) + return args.Get(0).([]byte), args.Error(1) +} + +func (m *mockAccessTokenBuilder) TokenType() string { + args := m.Called() + return args.String(0) +} + func newTestSteps(t *testing.T) *steps { dsc, err := gdatastore.NewClient(context.Background(), "test") require.NoError(t, err) return &steps{ - db: datastore.New(dsc), + db: datastore.New(dsc), + builder: &mockAccessTokenBuilder{}, } } @@ -751,39 +774,31 @@ func TestVerifyRefreshTokenErrors(t *testing.T) { } } -func TestSignAccessToken(t *testing.T) { +func TestBuildAccessToken(t *testing.T) { s := newTestSteps(t) - signKeyName := "refreshKey" - kms := kmssim.NewClient([]string{signKeyName}) - signKey, err := kmssign.NewKey(context.Background(), kms, signKeyName) - require.NoError(t, err) - now := time.Now() - data := &accessTokenData{ - clientID: "clientID", - userID: "userID", - userEmail: "userEmail", + data := &token.AccessTokenData{ + ClientID: "clientID", + UserID: "userID", + UserEmail: "userEmail", + IssueTime: now, + ExpireTime: now.Add(accessTokenDuration), } - accessToken, err := s.SignAccessToken(context.Background(), signKey, data, now) + expectedAccessToken := []byte("expected-access-token") + expectedTokenType := "MockBearer" + + s.builder.(*mockAccessTokenBuilder).On("Build", mock.Anything, testAudienceName, data).Return(expectedAccessToken, nil) + s.builder.(*mockAccessTokenBuilder).On("TokenType").Return(expectedTokenType) + + accessToken, tokenType, err := s.BuildAccessToken(context.Background(), testAudienceName, data) require.NoError(t, err) require.NotEmpty(t, accessToken) - - got := new(pb.AccessToken) + require.Equal(t, expectedTokenType, tokenType) accessTokenBytes, err := base64Decode(accessToken) require.NoError(t, err) - - require.NoError(t, signpb.VerifyUnmarshal(signKey, accessTokenBytes, got)) - require.Equal(t, data.clientID, got.ClientId) - require.Equal(t, data.userID, got.UserId) - require.Equal(t, data.userEmail, got.UserEmail) - - nowPb, _ := ptypes.TimestampProto(now) - require.Equal(t, nowPb, got.IssueTime) - - expirePb, _ := ptypes.TimestampProto(now.Add(accessTokenDuration)) - require.Equal(t, expirePb, got.ExpireTime) + require.Equal(t, expectedAccessToken, accessTokenBytes) } diff --git a/pkg/idp/token/bearer.go b/pkg/idp/token/bearer.go new file mode 100644 index 0000000..8ddb234 --- /dev/null +++ b/pkg/idp/token/bearer.go @@ -0,0 +1,50 @@ +package token + +import ( + "context" + "crypto" + "fmt" + + "github.com/flynn/hubauth/pkg/kmssign" + "github.com/flynn/hubauth/pkg/pb" + "github.com/flynn/hubauth/pkg/signpb" + "github.com/golang/protobuf/ptypes" +) + +type bearerBuilder struct { + kms kmssign.KMSClient + audienceKey kmssign.AudienceKeyNamer +} + +var _ AccessTokenBuilder = (*bearerBuilder)(nil) + +func NewBearerBuilder(kms kmssign.KMSClient, audienceKey kmssign.AudienceKeyNamer) AccessTokenBuilder { + return &bearerBuilder{ + kms: kms, + audienceKey: audienceKey, + } +} + +func (b *bearerBuilder) Build(ctx context.Context, audience string, t *AccessTokenData) ([]byte, error) { + signKey := kmssign.NewPrivateKey(b.kms, b.audienceKey(audience), crypto.SHA256) + + exp, _ := ptypes.TimestampProto(t.ExpireTime) + iss, _ := ptypes.TimestampProto(t.IssueTime) + msg := &pb.AccessToken{ + ClientId: t.ClientID, + UserId: t.UserID, + UserEmail: t.UserEmail, + IssueTime: iss, + ExpireTime: exp, + } + tokenBytes, err := signpb.SignMarshal(ctx, signKey, msg) + if err != nil { + return nil, fmt.Errorf("token: error signing access token: %w", err) + } + + return tokenBytes, nil +} + +func (b *bearerBuilder) TokenType() string { + return "Bearer" +} diff --git a/pkg/idp/token/bearer_test.go b/pkg/idp/token/bearer_test.go new file mode 100644 index 0000000..0789be4 --- /dev/null +++ b/pkg/idp/token/bearer_test.go @@ -0,0 +1,59 @@ +package token + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/flynn/hubauth/pkg/kmssign" + "github.com/flynn/hubauth/pkg/kmssign/kmssim" + "github.com/flynn/hubauth/pkg/pb" + "github.com/flynn/hubauth/pkg/signpb" + "github.com/golang/protobuf/ptypes" + "github.com/stretchr/testify/require" +) + +func audienceKeyNamer(s string) string { + return fmt.Sprintf("%s_named", s) +} + +func TestSignedPBBuilder(t *testing.T) { + audienceName := "audience_url" + audienceKeyName := audienceKeyNamer(audienceName) + kms := kmssim.NewClient([]string{audienceKeyName}) + + builder := NewBearerBuilder(kms, audienceKeyNamer) + + signKey, err := kmssign.NewKey(context.Background(), kms, audienceKeyName) + require.NoError(t, err) + + now := time.Now() + ctx := context.Background() + + accessTokenDuration := 5 * time.Minute + + data := &AccessTokenData{ + ClientID: "clientID", + UserEmail: "userEmail", + UserID: "userID", + IssueTime: now, + ExpireTime: now.Add(accessTokenDuration), + } + + accessTokenBytes, err := builder.Build(ctx, audienceName, data) + require.NoError(t, err) + + got := new(pb.AccessToken) + require.NoError(t, signpb.VerifyUnmarshal(signKey, accessTokenBytes, got)) + + require.Equal(t, data.ClientID, got.ClientId) + require.Equal(t, data.UserID, got.UserId) + require.Equal(t, data.UserEmail, got.UserEmail) + + nowPb, _ := ptypes.TimestampProto(now) + require.Equal(t, nowPb, got.IssueTime) + + expirePb, _ := ptypes.TimestampProto(now.Add(accessTokenDuration)) + require.Equal(t, expirePb, got.ExpireTime) +} diff --git a/pkg/idp/token/biscuit.go b/pkg/idp/token/biscuit.go new file mode 100644 index 0000000..9c997be --- /dev/null +++ b/pkg/idp/token/biscuit.go @@ -0,0 +1,65 @@ +package token + +import ( + "context" + "crypto" + "encoding/base64" + "errors" + "fmt" + + "github.com/flynn/biscuit-go/cookbook/signedbiscuit" + "github.com/flynn/biscuit-go/sig" + "github.com/flynn/hubauth/pkg/kmssign" +) + +var ( + ErrPublicKeyRequired = errors.New("token: a public key is required") +) + +type biscuitBuilder struct { + kms kmssign.KMSClient + audienceKey kmssign.AudienceKeyNamer + rootKeyPair sig.Keypair +} + +func NewBiscuitBuilder(kms kmssign.KMSClient, audienceKey kmssign.AudienceKeyNamer, rootKeyPair sig.Keypair) AccessTokenBuilder { + return &biscuitBuilder{ + kms: kms, + audienceKey: audienceKey, + rootKeyPair: rootKeyPair, + } +} + +func (b *biscuitBuilder) Build(ctx context.Context, audience string, t *AccessTokenData) ([]byte, error) { + if len(t.UserPublicKey) == 0 { + return nil, ErrPublicKeyRequired + } + audienceKey := kmssign.NewPrivateKey(b.kms, b.audienceKey(audience), crypto.SHA256) + + meta := &signedbiscuit.Metadata{ + ClientID: t.ClientID, + UserID: t.UserID, + UserEmail: t.UserEmail, + IssueTime: t.IssueTime, + } + + return signedbiscuit.GenerateSignable(b.rootKeyPair, audience, audienceKey, t.UserPublicKey, t.ExpireTime, meta) +} + +func (b *biscuitBuilder) TokenType() string { + return "Biscuit" +} + +func DecodeB64PrivateKey(b64key string) (sig.Keypair, error) { + var kp sig.Keypair + privKey, err := base64.StdEncoding.DecodeString(b64key) + if err != nil { + return kp, fmt.Errorf("failed to decode b64 key: %w", err) + } + rootPrivateKey, err := sig.NewPrivateKey(privKey) + if err != nil { + return kp, fmt.Errorf("failed to create biscuit private key: %w", err) + } + kp = sig.NewKeypair(rootPrivateKey) + return kp, nil +} diff --git a/pkg/idp/token/biscuit_test.go b/pkg/idp/token/biscuit_test.go new file mode 100644 index 0000000..41b705f --- /dev/null +++ b/pkg/idp/token/biscuit_test.go @@ -0,0 +1,63 @@ +package token + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "encoding/base64" + "testing" + "time" + + "github.com/flynn/biscuit-go" + "github.com/flynn/biscuit-go/sig" + "github.com/flynn/hubauth/pkg/kmssign/kmssim" + "github.com/stretchr/testify/require" +) + +func TestBiscuitBuilder(t *testing.T) { + audience := "https://audience.url" + audienceKeyName := audienceKeyNamer(audience) + kmsClient := kmssim.NewClient([]string{audienceKeyName}) + rootKeyPair := sig.GenerateKeypair(rand.Reader) + + builder := NewBiscuitBuilder(kmsClient, audienceKeyNamer, rootKeyPair) + + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + userPublicKey, err := x509.MarshalPKIXPublicKey(&priv.PublicKey) + require.NoError(t, err) + + now := time.Now() + accessTokenData := &AccessTokenData{ + ClientID: "clientID", + ExpireTime: now.Add(1 * time.Minute), + IssueTime: now, + UserEmail: "user@email", + UserID: "userID", + } + _, err = builder.Build(context.Background(), audience, accessTokenData) + require.Equal(t, ErrPublicKeyRequired, err) + + accessTokenData.UserPublicKey = userPublicKey + token, err := builder.Build(context.Background(), audience, accessTokenData) + require.NoError(t, err) + require.NotEmpty(t, token) + + b, err := biscuit.Unmarshal(token) + require.NoError(t, err) + + _, err = b.Verify(rootKeyPair.Public()) + require.NoError(t, err) +} + +func TestDecodeB64PrivateKey(t *testing.T) { + expectedKP := sig.GenerateKeypair(rand.Reader) + b64key := base64.StdEncoding.EncodeToString(expectedKP.Private().Bytes()) + + kp, err := DecodeB64PrivateKey(b64key) + require.NoError(t, err) + require.Equal(t, expectedKP.Private().Bytes(), kp.Private().Bytes()) + require.Equal(t, expectedKP.Public().Bytes(), kp.Public().Bytes()) +} diff --git a/pkg/idp/token/builder.go b/pkg/idp/token/builder.go new file mode 100644 index 0000000..0da8c9a --- /dev/null +++ b/pkg/idp/token/builder.go @@ -0,0 +1,20 @@ +package token + +import ( + "context" + "time" +) + +type AccessTokenData struct { + ClientID string + UserID string + UserEmail string + UserPublicKey []byte + IssueTime time.Time + ExpireTime time.Time +} + +type AccessTokenBuilder interface { + Build(ctx context.Context, audience string, t *AccessTokenData) ([]byte, error) + TokenType() string +} diff --git a/pkg/kmssign/kms.go b/pkg/kmssign/kms.go index 8671e3e..766ffe3 100644 --- a/pkg/kmssign/kms.go +++ b/pkg/kmssign/kms.go @@ -8,6 +8,8 @@ import ( "encoding/pem" "io" "math/big" + "net/url" + "strings" gax "github.com/googleapis/gax-go/v2" "golang.org/x/crypto/cryptobyte" @@ -16,6 +18,18 @@ import ( kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" ) +type AudienceKeyNamer func(audience string) string + +func AudienceKeyNameFunc(projectID, location, keyRing string) func(string) string { + return func(aud string) string { + u, err := url.Parse(aud) + if err != nil { + return "" + } + return fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/1", projectID, location, keyRing, strings.Replace(u.Host, ".", "_", -1)) + } +} + type KMSClient interface { AsymmetricSign(ctx context.Context, req *kmspb.AsymmetricSignRequest, opts ...gax.CallOption) (*kmspb.AsymmetricSignResponse, error) GetPublicKey(ctx context.Context, req *kmspb.GetPublicKeyRequest, opts ...gax.CallOption) (*kmspb.PublicKey, error)