chore: migrate to gitea
This commit is contained in:
34
.gitea/workflows/golangci-lint.yml
Normal file
34
.gitea/workflows/golangci-lint.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
name: golangci-lint
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
# Optional: allow read access to pull requests. Use with `only-new-issues` option.
|
||||||
|
# pull-requests: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
golangci:
|
||||||
|
name: lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # all history for all branches and tags
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
check-latest: true
|
||||||
|
cache: false
|
||||||
|
version: 1.24
|
||||||
|
- name: golangci-lint
|
||||||
|
uses: golangci/golangci-lint-action@v8
|
||||||
|
with:
|
||||||
|
version: v2.8.0
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
go build .
|
||||||
27
.gitea/workflows/test.yml
Normal file
27
.gitea/workflows/test.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # all history for all branches and tags
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
check-latest: true
|
||||||
|
cache: false
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y make
|
||||||
|
|
||||||
|
- name: Run make check-all
|
||||||
|
run: make test
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -13,5 +13,3 @@
|
|||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
budget*
|
budget*
|
||||||
|
|
||||||
vendor/
|
|
||||||
|
|||||||
37
cmd/root.go
37
cmd/root.go
@@ -1,12 +1,13 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/ginserver"
|
|
||||||
"budget/handler"
|
|
||||||
"budget/internal/utils"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/ginserver"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/handler"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@@ -47,7 +48,10 @@ var rootCmd = &cobra.Command{
|
|||||||
WithField(parameterDBConnectionURI, config.DBConnectionURI).
|
WithField(parameterDBConnectionURI, config.DBConnectionURI).
|
||||||
Warn("Configuration")
|
Warn("Configuration")
|
||||||
router := ginserver.NewRouter(config)
|
router := ginserver.NewRouter(config)
|
||||||
router.Run(fmt.Sprintf(":%d", config.Port))
|
err := router.Run(fmt.Sprintf(":%d", config.Port))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,19 +68,34 @@ func init() {
|
|||||||
rootCmd.PersistentFlags().StringVar(&cfgFile, parameterConfigurationFile, "", "Config file. All flags given in command line will override the values from this file.")
|
rootCmd.PersistentFlags().StringVar(&cfgFile, parameterConfigurationFile, "", "Config file. All flags given in command line will override the values from this file.")
|
||||||
|
|
||||||
rootCmd.Flags().String(parameterLogLevel, defaultLogLevel, "Use this flag to set the logging level")
|
rootCmd.Flags().String(parameterLogLevel, defaultLogLevel, "Use this flag to set the logging level")
|
||||||
viper.BindPFlag(parameterLogLevel, rootCmd.Flags().Lookup(parameterLogLevel))
|
err := viper.BindPFlag(parameterLogLevel, rootCmd.Flags().Lookup(parameterLogLevel))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
rootCmd.Flags().String(parameterLogFormat, defaultLogFormat, "Use this flag to set the logging format")
|
rootCmd.Flags().String(parameterLogFormat, defaultLogFormat, "Use this flag to set the logging format")
|
||||||
viper.BindPFlag(parameterLogFormat, rootCmd.Flags().Lookup(parameterLogFormat))
|
err = viper.BindPFlag(parameterLogFormat, rootCmd.Flags().Lookup(parameterLogFormat))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
rootCmd.Flags().String(parameterDBConnectionURI, defaultDBConnectionURI, "Use this flag to set the db connection URI")
|
rootCmd.Flags().String(parameterDBConnectionURI, defaultDBConnectionURI, "Use this flag to set the db connection URI")
|
||||||
viper.BindPFlag(parameterDBConnectionURI, rootCmd.Flags().Lookup(parameterDBConnectionURI))
|
err = viper.BindPFlag(parameterDBConnectionURI, rootCmd.Flags().Lookup(parameterDBConnectionURI))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
rootCmd.Flags().Int(parameterPort, defaultPort, "Use this flag to set the listening port of the api")
|
rootCmd.Flags().Int(parameterPort, defaultPort, "Use this flag to set the listening port of the api")
|
||||||
viper.BindPFlag(parameterPort, rootCmd.Flags().Lookup(parameterPort))
|
err = viper.BindPFlag(parameterPort, rootCmd.Flags().Lookup(parameterPort))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
rootCmd.Flags().Bool(parameterMock, false, "Use this flag to enable the mock mode")
|
rootCmd.Flags().Bool(parameterMock, false, "Use this flag to enable the mock mode")
|
||||||
viper.BindPFlag(parameterMock, rootCmd.Flags().Lookup(parameterMock))
|
err = viper.BindPFlag(parameterMock, rootCmd.Flags().Lookup(parameterMock))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// initConfig reads in config file and ENV variables if set.
|
// initConfig reads in config file and ENV variables if set.
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package ginserver
|
package ginserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/internal/utils"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
package ginserver
|
package ginserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/handler"
|
|
||||||
"budget/internal/account"
|
|
||||||
"budget/internal/expense"
|
|
||||||
"budget/internal/jointaccount"
|
|
||||||
"budget/internal/storage/dao/postgresql"
|
|
||||||
"budget/internal/user"
|
|
||||||
"budget/middleware"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/handler"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/account"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/expense"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao/postgresql"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/user"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/middleware"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
"github.com/gin-contrib/cors"
|
"github.com/gin-contrib/cors"
|
||||||
@@ -38,7 +38,6 @@ func NewRouter(config *handler.Config) *gin.Engine {
|
|||||||
hc := handler.NewContext()
|
hc := handler.NewContext()
|
||||||
uh := user.NewHandler(hc, db)
|
uh := user.NewHandler(hc, db)
|
||||||
ah := account.NewHandler(hc, db)
|
ah := account.NewHandler(hc, db)
|
||||||
jah := jointaccount.NewHandler(hc, db)
|
|
||||||
eh := expense.NewHandler(hc, db)
|
eh := expense.NewHandler(hc, db)
|
||||||
mv := middleware.NewValidator(hc, db)
|
mv := middleware.NewValidator(hc, db)
|
||||||
public := router.Group("/")
|
public := router.Group("/")
|
||||||
@@ -71,17 +70,5 @@ func NewRouter(config *handler.Config) *gin.Engine {
|
|||||||
securedExistingExpenses := securedValidAccount.Group("/expenses/:expenseId")
|
securedExistingExpenses := securedValidAccount.Group("/expenses/:expenseId")
|
||||||
securedExistingExpenses.Handle(http.MethodGet, "", eh.GetAnExpenses)
|
securedExistingExpenses.Handle(http.MethodGet, "", eh.GetAnExpenses)
|
||||||
securedExistingExpenses.Handle(http.MethodDelete, "", eh.DeleteExpense)
|
securedExistingExpenses.Handle(http.MethodDelete, "", eh.DeleteExpense)
|
||||||
|
|
||||||
//account route
|
|
||||||
securedMatchingToken.Handle(http.MethodGet, "/jointaccounts", jah.GetAllJointaccountOfUser)
|
|
||||||
securedMatchingToken.Handle(http.MethodPost, "/jointaccounts", jah.CreateJointaccountOfUser)
|
|
||||||
|
|
||||||
securedValidJointAccount := securedMatchingToken.Group("/jointaccounts/:jointaccountId")
|
|
||||||
securedValidJointAccount.Use(mv.HasValidJointAccountId)
|
|
||||||
securedValidJointAccount.Use(mv.JointAccountExists)
|
|
||||||
securedValidJointAccount.Handle(http.MethodDelete, "", jah.DeleteJointaccountOfUser)
|
|
||||||
securedValidJointAccount.Handle(http.MethodGet, "", jah.GetSpecificJointaccountOfUser)
|
|
||||||
securedValidJointAccount.Handle(http.MethodPost, "/expenses", eh.CreateAnExpense)
|
|
||||||
securedValidJointAccount.Handle(http.MethodGet, "/expenses", eh.GetAllExpenses)
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|||||||
113
go.mod
113
go.mod
@@ -1,54 +1,75 @@
|
|||||||
module budget
|
module gitea.frenchtouch.duckdns.org/kratisto/budget-backend
|
||||||
|
|
||||||
go 1.17
|
go 1.25
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gin-contrib/cors v1.3.1
|
github.com/gin-contrib/cors v1.7.6
|
||||||
github.com/gin-gonic/gin v1.7.4
|
github.com/gin-gonic/gin v1.11.0
|
||||||
github.com/lib/pq v1.10.3
|
github.com/lib/pq v1.10.9
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.9.4
|
||||||
github.com/spf13/cobra v1.2.1
|
github.com/spf13/cobra v1.10.2
|
||||||
github.com/spf13/viper v1.9.0
|
github.com/spf13/viper v1.21.0
|
||||||
google.golang.org/api v0.59.0
|
google.golang.org/api v0.262.0
|
||||||
gopkg.in/go-playground/validator.v9 v9.31.0
|
gopkg.in/go-playground/validator.v9 v9.31.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.97.0 // indirect
|
cloud.google.com/go/auth v0.18.1 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||||
github.com/go-playground/locales v0.13.0 // indirect
|
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||||
github.com/go-playground/universal-translator v0.17.0 // indirect
|
github.com/bytedance/sonic v1.15.0 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.4.1 // indirect
|
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.1.1 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||||
github.com/json-iterator/go v1.1.11 // indirect
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
github.com/leodido/go-urn v1.2.0 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
github.com/magiconair/properties v1.8.5 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.4.2 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
github.com/go-playground/validator/v10 v10.30.1 // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/spf13/afero v1.6.0 // indirect
|
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||||
github.com/spf13/cast v1.4.1 // indirect
|
github.com/google/s2a-go v0.1.9 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect
|
||||||
github.com/subosito/gotenv v1.2.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.16.0 // indirect
|
||||||
github.com/ugorji/go/codec v1.1.7 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
go.opencensus.io v0.23.0 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
golang.org/x/text v0.3.6 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20211008145708-270636b82663 // indirect
|
github.com/quic-go/qpack v0.6.0 // indirect
|
||||||
google.golang.org/grpc v1.40.0 // indirect
|
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||||
google.golang.org/protobuf v1.27.1 // indirect
|
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.63.2 // indirect
|
github.com/spf13/afero v1.15.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.10 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||||
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.39.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.39.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.39.0 // indirect
|
||||||
|
go.uber.org/mock v0.6.0 // indirect
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
|
golang.org/x/arch v0.23.0 // indirect
|
||||||
|
golang.org/x/crypto v0.47.0 // indirect
|
||||||
|
golang.org/x/net v0.49.0 // indirect
|
||||||
|
golang.org/x/oauth2 v0.34.0 // indirect
|
||||||
|
golang.org/x/sys v0.40.0 // indirect
|
||||||
|
golang.org/x/text v0.33.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect
|
||||||
|
google.golang.org/grpc v1.78.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
|
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
916
go.sum
916
go.sum
@@ -1,759 +1,187 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs=
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA=
|
||||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
|
||||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
|
||||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
|
||||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
|
||||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
|
||||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
|
||||||
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
|
|
||||||
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
|
|
||||||
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
|
|
||||||
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
|
||||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
|
||||||
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
|
|
||||||
cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8=
|
|
||||||
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
|
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
|
||||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
|
||||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
|
||||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
|
||||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
|
||||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
|
||||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
|
||||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
|
||||||
cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU=
|
|
||||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
|
||||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
|
||||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
|
||||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
|
||||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
|
||||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
|
||||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
|
||||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
|
||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
|
||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
|
||||||
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
|
||||||
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/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
|
||||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
|
||||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
|
||||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||||
github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
|
||||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
|
||||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
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=
|
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
|
||||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
|
||||||
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/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
|
||||||
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/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/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/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
|
||||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao=
|
||||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8=
|
||||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y=
|
||||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14=
|
||||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
|
||||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
|
||||||
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
|
|
||||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
|
||||||
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
|
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
|
||||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
|
||||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
|
||||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
|
||||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
|
||||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
|
||||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
|
||||||
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/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
|
||||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
|
||||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
|
||||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
|
||||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
|
||||||
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
|
||||||
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
|
||||||
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/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
|
||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
|
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
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/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
|
||||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
|
||||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
|
||||||
github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
|
|
||||||
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
|
||||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
|
||||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
|
||||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
|
||||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
|
||||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
|
||||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
|
||||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
|
||||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
|
||||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
|
||||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
|
||||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
|
||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
|
||||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
|
||||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
|
||||||
github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo=
|
|
||||||
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||||
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE=
|
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||||
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
|
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||||
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
|
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
|
||||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
||||||
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
|
||||||
github.com/spf13/viper v1.9.0 h1:yR6EXjTp0y0cLN8OZg1CRZmOBdI88UcGkhgyJhu6nZk=
|
|
||||||
github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
||||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
||||||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
||||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
||||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
|
||||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
google.golang.org/api v0.262.0 h1:4B+3u8He2GwyN8St3Jhnd3XRHlIvc//sBmgHSp78oNY=
|
||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
google.golang.org/api v0.262.0/go.mod h1:jNwmH8BgUBJ/VrUG6/lIl9YiildyLd09r9ZLHiQ6cGI=
|
||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 h1:GvESR9BIyHUahIb0NcTum6itIWtdoglGX+rnGxm2934=
|
||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:yJ2HH4EHEDTd3JiLmhds6NkJ17ITVYOdV3m3VKOnws0=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
|
||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
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/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
|
||||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
|
||||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
|
||||||
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/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.4.2/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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
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=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
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/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
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/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/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=
|
|
||||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
|
||||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 h1:a8jGStKg0XqKDlKqjLrXn0ioF5MH36pT7Z0BRTqLhbk=
|
|
||||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
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/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
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/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 h1:B333XXssMuKQeBwiNODx4TupZy7bf4sxFZnN2ZOcvUE=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
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=
|
|
||||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac h1:oN6lz7iLW/YC7un8pq+9bOLyXrprv2+DKfkJY+2LJJw=
|
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
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/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
|
||||||
golang.org/x/text v0.3.6/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/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=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
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/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=
|
|
||||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
|
||||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
|
||||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
|
||||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
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-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
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-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/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
|
||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|
||||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|
||||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|
||||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|
||||||
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/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=
|
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
|
||||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
|
||||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
|
||||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
|
||||||
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/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
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/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
|
||||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
|
||||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
|
||||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
|
||||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
|
||||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
|
||||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
|
||||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
|
||||||
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
|
|
||||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
|
||||||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
|
|
||||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
|
||||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
|
||||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
|
||||||
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
|
||||||
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
|
||||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
|
|
||||||
google.golang.org/api v0.59.0 h1:fPfFO7gttlXYo2ALuD3HxJzh8vaF++4youI0BkFL6GE=
|
|
||||||
google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
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.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
|
||||||
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=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
|
||||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
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/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
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=
|
|
||||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
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-20200513103714-09dca8ec2884/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/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
|
||||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
|
||||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
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-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/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
|
||||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
|
||||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
|
||||||
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
|
||||||
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
|
||||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
|
||||||
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
|
||||||
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
|
||||||
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
|
||||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
|
||||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
|
||||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
|
||||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
|
||||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
|
||||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
|
||||||
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
|
||||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
|
||||||
google.golang.org/genproto v0.0.0-20211008145708-270636b82663 h1:33YYJanAOLxgS7pGjzC1IUrFLpwN//bbgtnKYikOmQU=
|
|
||||||
google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
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/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
|
||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
|
||||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
|
||||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
|
||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
|
||||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
|
||||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
|
||||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
|
||||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
|
||||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
|
||||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
|
||||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
|
||||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
|
||||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
|
||||||
google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
|
|
||||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
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/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
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/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
|
||||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
|
||||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
|
||||||
gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M=
|
gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M=
|
||||||
gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|
||||||
gopkg.in/ini.v1 v1.63.2 h1:tGK/CyBg7SMzb60vP1M03vNZ3VDu3wGQJwn7Sxi9r3c=
|
|
||||||
gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
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/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
|
||||||
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/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/internal/storage/validators"
|
|
||||||
"budget/internal/utils"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/validators"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gopkg.in/go-playground/validator.v9"
|
"gopkg.in/go-playground/validator.v9"
|
||||||
)
|
)
|
||||||
@@ -39,7 +40,10 @@ func newValidator() *validator.Validate {
|
|||||||
|
|
||||||
for k, v := range validators.CustomValidators {
|
for k, v := range validators.CustomValidators {
|
||||||
if v.Validator != nil {
|
if v.Validator != nil {
|
||||||
va.RegisterValidationCtx(k, v.Validator)
|
err := va.RegisterValidationCtx(k, v.Validator)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package account
|
package account
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/internal/storage/dao"
|
"database/sql"
|
||||||
"budget/internal/storage/dao/postgresql"
|
"errors"
|
||||||
"budget/internal/utils"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao/postgresql"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"
|
||||||
|
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,7 +26,12 @@ func (db *Database) GetAllAccountOfUser(id string) ([]*Account, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer func(rows *sql.Rows) {
|
||||||
|
err := rows.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(rows)
|
||||||
|
|
||||||
as := make([]*Account, 0)
|
as := make([]*Account, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
@@ -45,15 +53,22 @@ func (db *Database) GetAccountWithNameForUser(name string, id string) (*Account,
|
|||||||
AND a.name = $2
|
AND a.name = $2
|
||||||
`
|
`
|
||||||
row, err := db.Session.Query(q, id, name)
|
row, err := db.Session.Query(q, id, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if !row.Next() {
|
if !row.Next() {
|
||||||
return nil, dao.NewDAOError(dao.ErrTypeNotFound, fmt.Errorf("No row found"))
|
return nil, dao.NewDAOError(dao.ErrTypeNotFound, fmt.Errorf("no row found"))
|
||||||
}
|
}
|
||||||
a := Account{}
|
a := Account{}
|
||||||
row.Scan(&a.ID, &a.UserId, &a.Name, &a.Provider, &a.CreatedAt, &a.UpdatedAt)
|
err = row.Scan(&a.ID, &a.UserId, &a.Name, &a.Provider, &a.CreatedAt, &a.UpdatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if row.Next() {
|
if row.Next() {
|
||||||
return nil, fmt.Errorf("Impossibru")
|
return nil, fmt.Errorf("Impossibru")
|
||||||
}
|
}
|
||||||
if errPq, ok := err.(*pq.Error); ok {
|
var errPq *pq.Error
|
||||||
|
if errors.As(err, &errPq) {
|
||||||
return nil, postgresql.HandlePgError(errPq)
|
return nil, postgresql.HandlePgError(errPq)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package account
|
package account
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/handler"
|
|
||||||
"budget/internal/storage/dao/postgresql"
|
|
||||||
"budget/internal/storage/model"
|
|
||||||
"budget/internal/storage/validators"
|
|
||||||
"budget/internal/user"
|
|
||||||
"budget/internal/utils"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/handler"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao/postgresql"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/model"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/validators"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/user"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -75,13 +76,15 @@ func (c *Context) CreateAccountOfUser(gc *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
utils.JSON(gc.Writer, http.StatusCreated, accountSaved)
|
utils.JSON(gc.Writer, http.StatusCreated, accountSaved)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) DeleteAccountOfUser(gc *gin.Context) {
|
func (c *Context) DeleteAccountOfUser(gc *gin.Context) {
|
||||||
userId := gc.Param("userId")
|
userId := gc.Param("userId")
|
||||||
accountId := gc.Param("accountId")
|
accountId := gc.Param("accountId")
|
||||||
c.service.DeleteAccountOfUser(userId, accountId)
|
err := c.service.DeleteAccountOfUser(userId, accountId)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package account
|
package account
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/internal/storage/dao"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao"
|
||||||
"budget/internal/storage/model"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
@@ -12,8 +12,8 @@ type Service struct {
|
|||||||
func (s *Service) GetAllAccountOfUser(userId string) ([]*Account, error) {
|
func (s *Service) GetAllAccountOfUser(userId string) ([]*Account, error) {
|
||||||
accounts, err := s.db.GetAllAccountOfUser(userId)
|
accounts, err := s.db.GetAllAccountOfUser(userId)
|
||||||
if e, ok := err.(*dao.Error); ok {
|
if e, ok := err.(*dao.Error); ok {
|
||||||
switch {
|
switch e.Type {
|
||||||
case e.Type == dao.ErrTypeNotFound:
|
case dao.ErrTypeNotFound:
|
||||||
return nil, &model.ErrNotFound
|
return nil, &model.ErrNotFound
|
||||||
default:
|
default:
|
||||||
return nil, &model.ErrInternalServer
|
return nil, &model.ErrInternalServer
|
||||||
@@ -31,8 +31,8 @@ func (s *Service) GetAllAccountOfUser(userId string) ([]*Account, error) {
|
|||||||
func (s *Service) GetAccountWithNameForUser(name string, id string) (*Account, error) {
|
func (s *Service) GetAccountWithNameForUser(name string, id string) (*Account, error) {
|
||||||
account, err := s.db.GetAccountWithNameForUser(name, id)
|
account, err := s.db.GetAccountWithNameForUser(name, id)
|
||||||
if e, ok := err.(*dao.Error); ok {
|
if e, ok := err.(*dao.Error); ok {
|
||||||
switch {
|
switch e.Type {
|
||||||
case e.Type == dao.ErrTypeNotFound:
|
case dao.ErrTypeNotFound:
|
||||||
return nil, &model.ErrNotFound
|
return nil, &model.ErrNotFound
|
||||||
default:
|
default:
|
||||||
return nil, &model.ErrInternalServer
|
return nil, &model.ErrInternalServer
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package expense
|
package expense
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/internal/storage/dao/postgresql"
|
"database/sql"
|
||||||
"budget/internal/utils"
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao/postgresql"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"
|
||||||
|
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
)
|
)
|
||||||
@@ -43,7 +45,12 @@ func (db Database) GetExpensesOfAnAccountBetween(id, from, to string) ([]*Expens
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer func(rows *sql.Rows) {
|
||||||
|
err := rows.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(rows)
|
||||||
|
|
||||||
es := make([]*Expense, 0)
|
es := make([]*Expense, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
@@ -67,7 +74,12 @@ func (db Database) GetAllExpensesOfAnAccount(id string) ([]*Expense, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer func(rows *sql.Rows) {
|
||||||
|
err := rows.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(rows)
|
||||||
|
|
||||||
es := make([]*Expense, 0)
|
es := make([]*Expense, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package expense
|
package expense
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/handler"
|
|
||||||
"budget/internal/account"
|
|
||||||
"budget/internal/storage/dao/postgresql"
|
|
||||||
"budget/internal/storage/model"
|
|
||||||
"budget/internal/storage/validators"
|
|
||||||
"budget/internal/utils"
|
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/handler"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/account"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao/postgresql"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/model"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/validators"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ func (c *Context) CreateAnExpense(gc *gin.Context) {
|
|||||||
csvr := csv.NewReader(csvFile)
|
csvr := csv.NewReader(csvFile)
|
||||||
csvr.FieldsPerRecord = -1
|
csvr.FieldsPerRecord = -1
|
||||||
csvr.Comma = ';'
|
csvr.Comma = ';'
|
||||||
filedata, err := csvr.ReadAll()
|
filedata, _ := csvr.ReadAll()
|
||||||
|
|
||||||
account, err := c.accountService.GetASpecificAccountForUser(userId, accountID)
|
account, err := c.accountService.GetASpecificAccountForUser(userId, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package expense
|
package expense
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/internal/account"
|
|
||||||
"budget/internal/storage/dao"
|
|
||||||
"budget/internal/storage/model"
|
|
||||||
"budget/internal/utils"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/account"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/model"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
@@ -18,8 +19,8 @@ func (s Service) GetExpensesOfAnAccountBetween(accountId, from, to string) ([]*E
|
|||||||
expenses, err := s.db.GetExpensesOfAnAccountBetween(accountId, from, to)
|
expenses, err := s.db.GetExpensesOfAnAccountBetween(accountId, from, to)
|
||||||
utils.GetLogger().Info(err)
|
utils.GetLogger().Info(err)
|
||||||
if e, ok := err.(*dao.Error); ok {
|
if e, ok := err.(*dao.Error); ok {
|
||||||
switch {
|
switch e.Type {
|
||||||
case e.Type == dao.ErrTypeNotFound:
|
case dao.ErrTypeNotFound:
|
||||||
return nil, &model.ErrNotFound
|
return nil, &model.ErrNotFound
|
||||||
default:
|
default:
|
||||||
return nil, &model.ErrInternalServer
|
return nil, &model.ErrInternalServer
|
||||||
@@ -38,8 +39,8 @@ func (s Service) GetAllExpensesOfAnAccount(accountId string) ([]*Expense, error)
|
|||||||
expenses, err := s.db.GetAllExpensesOfAnAccount(accountId)
|
expenses, err := s.db.GetAllExpensesOfAnAccount(accountId)
|
||||||
utils.GetLogger().Info(err)
|
utils.GetLogger().Info(err)
|
||||||
if e, ok := err.(*dao.Error); ok {
|
if e, ok := err.(*dao.Error); ok {
|
||||||
switch {
|
switch e.Type {
|
||||||
case e.Type == dao.ErrTypeNotFound:
|
case dao.ErrTypeNotFound:
|
||||||
return nil, &model.ErrNotFound
|
return nil, &model.ErrNotFound
|
||||||
default:
|
default:
|
||||||
return nil, &model.ErrInternalServer
|
return nil, &model.ErrInternalServer
|
||||||
@@ -100,7 +101,10 @@ func (s Service) processCaisseEpargne(filedata [][]string, account *account.Acco
|
|||||||
},
|
},
|
||||||
AccountId: account.ID,
|
AccountId: account.ID,
|
||||||
}
|
}
|
||||||
s.CreateExpense(expense)
|
err = s.CreateExpense(expense)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
utils.GetLogger().Info(val)
|
utils.GetLogger().Info(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
package jointaccount
|
|
||||||
|
|
||||||
import (
|
|
||||||
"budget/internal/storage/dao"
|
|
||||||
"budget/internal/storage/dao/postgresql"
|
|
||||||
"budget/internal/utils"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/lib/pq"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Database struct {
|
|
||||||
*postgresql.DatabasePostgreSQL
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) GetAllJointaccountOfUser(id string) ([]*Jointaccount, error) {
|
|
||||||
q := `
|
|
||||||
SELECT a.id, a.user_id, a.name, a.provider, a.created_at, a.updated_at
|
|
||||||
FROM public.jointaccount a
|
|
||||||
WHERE a.user_id = $1
|
|
||||||
`
|
|
||||||
rows, err := db.Session.Query(q, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
as := make([]*Jointaccount, 0)
|
|
||||||
for rows.Next() {
|
|
||||||
a := Jointaccount{}
|
|
||||||
err := rows.Scan(&a.ID, &a.UserId, &a.Name, &a.Provider, &a.CreatedAt, &a.UpdatedAt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
as = append(as, &a)
|
|
||||||
}
|
|
||||||
return as, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) GetJointaccountWithNameForUser(name string, id string) (*Jointaccount, error) {
|
|
||||||
q := `
|
|
||||||
SELECT a.id, a.user_id, a.name, a.provider, a.created_at, a.updated_at
|
|
||||||
FROM public.jointaccount a
|
|
||||||
WHERE a.user_id = $1
|
|
||||||
AND a.name = $2
|
|
||||||
`
|
|
||||||
row, err := db.Session.Query(q, id, name)
|
|
||||||
if !row.Next() {
|
|
||||||
return nil, dao.NewDAOError(dao.ErrTypeNotFound, fmt.Errorf("No row found"))
|
|
||||||
}
|
|
||||||
a := Jointaccount{}
|
|
||||||
row.Scan(&a.ID, &a.UserId, &a.Name, &a.Provider, &a.CreatedAt, &a.UpdatedAt)
|
|
||||||
if row.Next() {
|
|
||||||
return nil, fmt.Errorf("Impossibru")
|
|
||||||
}
|
|
||||||
if errPq, ok := err.(*pq.Error); ok {
|
|
||||||
return nil, postgresql.HandlePgError(errPq)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
utils.GetLogger().Info(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &a, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) CreateJointaccount(jointaccount *Jointaccount) error {
|
|
||||||
q := `
|
|
||||||
INSERT INTO public.jointaccount
|
|
||||||
(Name, Provider, user_id)
|
|
||||||
VALUES
|
|
||||||
($1, $2, $3)
|
|
||||||
RETURNING id, created_at
|
|
||||||
`
|
|
||||||
|
|
||||||
err := db.Session.
|
|
||||||
QueryRow(q, jointaccount.Name, jointaccount.Provider, jointaccount.UserId).
|
|
||||||
Scan(&jointaccount.ID, &jointaccount.CreatedAt)
|
|
||||||
if errPq, ok := err.(*pq.Error); ok {
|
|
||||||
return postgresql.HandlePgError(errPq)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) DeleteJointaccountOfAnUser(userId, jointaccountId string) error {
|
|
||||||
query := `
|
|
||||||
DELETE FROM jointaccount
|
|
||||||
WHERE user_id = $1
|
|
||||||
AND id = $2;`
|
|
||||||
_, err := db.Session.Exec(query, userId, jointaccountId)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) GetASpecificJointaccountForUser(userId, jointaccountId string) (*Jointaccount, error) {
|
|
||||||
q := `
|
|
||||||
SELECT a.id, a.user_id, a.name, a.provider, a.created_at, a.updated_at
|
|
||||||
FROM public.jointaccount a
|
|
||||||
WHERE a.user_id = $1
|
|
||||||
AND a.id = $2
|
|
||||||
`
|
|
||||||
row := db.Session.QueryRow(q, userId, jointaccountId)
|
|
||||||
a := Jointaccount{}
|
|
||||||
err := row.Scan(&a.ID, &a.UserId, &a.Name, &a.Provider, &a.CreatedAt, &a.UpdatedAt)
|
|
||||||
if errPq, ok := err.(*pq.Error); ok {
|
|
||||||
return nil, postgresql.HandlePgError(errPq)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
utils.GetLogger().Info(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &a, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDatabase(db *postgresql.DatabasePostgreSQL) *Database {
|
|
||||||
return &Database{db}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
package jointaccount
|
|
||||||
|
|
||||||
import (
|
|
||||||
"budget/handler"
|
|
||||||
"budget/internal/storage/dao/postgresql"
|
|
||||||
"budget/internal/storage/model"
|
|
||||||
"budget/internal/storage/validators"
|
|
||||||
"budget/internal/user"
|
|
||||||
"budget/internal/utils"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Context struct {
|
|
||||||
service *Service
|
|
||||||
db *Database
|
|
||||||
userService *user.Service
|
|
||||||
*handler.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) GetAllJointaccountOfUser(gc *gin.Context) {
|
|
||||||
userId := gc.Param("userId")
|
|
||||||
jointaccounts, err := c.service.GetAllJointaccountOfUser(userId)
|
|
||||||
if e, ok := err.(*model.APIError); ok {
|
|
||||||
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetAllJointaccounts: get jointaccounts")
|
|
||||||
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
|
|
||||||
} else if err != nil {
|
|
||||||
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get jointaccounts")
|
|
||||||
utils.JSONError(gc.Writer, model.ErrInternalServer)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(jointaccounts) == 0 {
|
|
||||||
utils.JSON(gc.Writer, http.StatusNoContent, nil)
|
|
||||||
} else {
|
|
||||||
utils.JSON(gc.Writer, http.StatusOK, jointaccounts)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) CreateJointaccountOfUser(gc *gin.Context) {
|
|
||||||
userId := gc.Param("userId")
|
|
||||||
|
|
||||||
var jointaccount Jointaccount
|
|
||||||
var jointaccountEditable JointaccountEditable
|
|
||||||
if err := gc.BindJSON(&jointaccountEditable); err != nil {
|
|
||||||
utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
jointaccount = Jointaccount{JointaccountEditable: jointaccountEditable, UserId: userId}
|
|
||||||
jointaccountFound, err := c.service.GetJointaccountWithNameForUser(jointaccount.Name, userId)
|
|
||||||
if e, ok := err.(*model.APIError); ok {
|
|
||||||
if e.Type != model.ErrNotFound.Type {
|
|
||||||
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetJointaccount: get jointaccount error")
|
|
||||||
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if err != nil {
|
|
||||||
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get jointaccount")
|
|
||||||
utils.JSONError(gc.Writer, model.ErrInternalServer)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if jointaccountFound != nil {
|
|
||||||
utils.GetLoggerFromCtx(gc).WithError(&model.ErrAlreadyExists).WithField("type", model.ErrAlreadyExists.Type).Error("error CreateJointaccount: jointaccount already exists")
|
|
||||||
utils.JSONErrorWithMessage(gc.Writer, model.ErrAlreadyExists, "jointaccount already exists with the same Name")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
jointaccount.UserId = userId
|
|
||||||
jointaccountSaved, err := c.service.CreateJointaccount(jointaccount)
|
|
||||||
if err != nil {
|
|
||||||
utils.GetLogger().Info(err)
|
|
||||||
utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
utils.JSON(gc.Writer, http.StatusCreated, jointaccountSaved)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) DeleteJointaccountOfUser(gc *gin.Context) {
|
|
||||||
userId := gc.Param("userId")
|
|
||||||
jointaccountId := gc.Param("jointaccountId")
|
|
||||||
c.service.DeleteJointaccountOfUser(userId, jointaccountId)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) GetSpecificJointaccountOfUser(gc *gin.Context) {
|
|
||||||
userId := gc.Param("userId")
|
|
||||||
jointaccountId := gc.Param("jointaccountId")
|
|
||||||
jointaccount, _ := c.service.GetASpecificJointaccountForUser(userId, jointaccountId)
|
|
||||||
utils.JSON(gc.Writer, http.StatusOK, jointaccount)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context {
|
|
||||||
database := NewDatabase(db)
|
|
||||||
service := NewService(database)
|
|
||||||
userService := user.NewService(user.NewDatabase(db))
|
|
||||||
return &Context{service: service, db: database, userService: userService, Context: ctx}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package jointaccount
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type Jointaccount struct {
|
|
||||||
JointaccountEditable
|
|
||||||
UserId string `json:"userId,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JointaccountEditable struct {
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Provider string `json:"provider"`
|
|
||||||
CreatedAt time.Time `json:"createdAt,omitempty"`
|
|
||||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package jointaccount
|
|
||||||
|
|
||||||
import (
|
|
||||||
"budget/internal/storage/dao"
|
|
||||||
"budget/internal/storage/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Service struct {
|
|
||||||
db *Database
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetAllJointaccountOfUser(userId string) ([]*Jointaccount, error) {
|
|
||||||
jointaccounts, err := s.db.GetAllJointaccountOfUser(userId)
|
|
||||||
if e, ok := err.(*dao.Error); ok {
|
|
||||||
switch {
|
|
||||||
case e.Type == dao.ErrTypeNotFound:
|
|
||||||
return nil, &model.ErrNotFound
|
|
||||||
default:
|
|
||||||
return nil, &model.ErrInternalServer
|
|
||||||
}
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, &model.ErrInternalServer
|
|
||||||
}
|
|
||||||
|
|
||||||
if jointaccounts == nil {
|
|
||||||
return nil, &model.ErrNotFound
|
|
||||||
}
|
|
||||||
return jointaccounts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetJointaccountWithNameForUser(name string, id string) (*Jointaccount, error) {
|
|
||||||
jointaccount, err := s.db.GetJointaccountWithNameForUser(name, id)
|
|
||||||
if e, ok := err.(*dao.Error); ok {
|
|
||||||
switch {
|
|
||||||
case e.Type == dao.ErrTypeNotFound:
|
|
||||||
return nil, &model.ErrNotFound
|
|
||||||
default:
|
|
||||||
return nil, &model.ErrInternalServer
|
|
||||||
}
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, &model.ErrInternalServer
|
|
||||||
}
|
|
||||||
return jointaccount, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) CreateJointaccount(jointaccount Jointaccount) (*Jointaccount, error) {
|
|
||||||
err := s.db.CreateJointaccount(&jointaccount)
|
|
||||||
return &jointaccount, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) DeleteJointaccountOfUser(userId, jointaccountId string) error {
|
|
||||||
return s.db.DeleteJointaccountOfAnUser(userId, jointaccountId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetASpecificJointaccountForUser(userId, jointaccountId string) (*Jointaccount, error) {
|
|
||||||
return s.db.GetASpecificJointaccountForUser(userId, jointaccountId)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewService(database *Database) *Service {
|
|
||||||
return &Service{db: database}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package jointexpense
|
|
||||||
|
|
||||||
import (
|
|
||||||
"budget/internal/storage/dao/postgresql"
|
|
||||||
"budget/internal/utils"
|
|
||||||
|
|
||||||
"github.com/lib/pq"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Database struct {
|
|
||||||
*postgresql.DatabasePostgreSQL
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *Database) CreateJointexpense(jointexpense *Jointexpense) error {
|
|
||||||
q := `
|
|
||||||
INSERT INTO public.jointexpense
|
|
||||||
(jointaccount_id, value, type_jointexpense, jointexpense_date, libelle)
|
|
||||||
VALUES
|
|
||||||
($1, $2, $3, $4, $5)
|
|
||||||
RETURNING id, created_at
|
|
||||||
`
|
|
||||||
|
|
||||||
err := db.Session.
|
|
||||||
QueryRow(q, jointexpense.JointaccountId, jointexpense.Value, jointexpense.TypeJointexpense, jointexpense.JointexpenseDate, jointexpense.Libelle).
|
|
||||||
Scan(&jointexpense.ID, &jointexpense.CreatedAt)
|
|
||||||
if err != nil {
|
|
||||||
utils.GetLogger().Info(err)
|
|
||||||
}
|
|
||||||
if errPq, ok := err.(*pq.Error); ok {
|
|
||||||
return postgresql.HandlePgError(errPq)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db Database) GetJointexpensesOfAnJointaccountBetween(id, from, to string) ([]*Jointexpense, error) {
|
|
||||||
q := `
|
|
||||||
SELECT a.id, a.jointaccount_id, a.value, a.type_jointexpense, a.jointexpense_date, a.created_at, a.updated_at, a.libelle
|
|
||||||
FROM public.jointexpense a
|
|
||||||
WHERE a.jointaccount_id = $1
|
|
||||||
AND a.jointexpense_date BETWEEN $2 and $3
|
|
||||||
`
|
|
||||||
rows, err := db.Session.Query(q, id, from, to)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
es := make([]*Jointexpense, 0)
|
|
||||||
for rows.Next() {
|
|
||||||
e := Jointexpense{}
|
|
||||||
err := rows.Scan(&e.ID, &e.JointjointaccountId, &e.Value, &e.TypeJointexpense, &e.JointexpenseDate, &e.CreatedAt, &e.UpdatedAt, &e.Libelle)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
es = append(es, &e)
|
|
||||||
}
|
|
||||||
return es, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db Database) GetAllJointexpensesOfAnJointaccount(id string) ([]*Jointexpense, error) {
|
|
||||||
q := `
|
|
||||||
SELECT a.id, a.jointaccount_id, a.value, a.type_jointexpense, a.jointexpense_date, a.created_at, a.updated_at, a.libelle
|
|
||||||
FROM public.jointexpense a
|
|
||||||
WHERE a.jointaccount_id = $1
|
|
||||||
`
|
|
||||||
rows, err := db.Session.Query(q, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
es := make([]*Jointexpense, 0)
|
|
||||||
for rows.Next() {
|
|
||||||
e := Jointexpense{}
|
|
||||||
err := rows.Scan(&e.ID, &e.JointaccountId, &e.Value, &e.TypeJointexpense, &e.JointexpenseDate, &e.CreatedAt, &e.UpdatedAt, &e.Libelle)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
es = append(es, &e)
|
|
||||||
}
|
|
||||||
return es, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDatabase(db *postgresql.DatabasePostgreSQL) *Database {
|
|
||||||
return &Database{db}
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
package jointexpense
|
|
||||||
|
|
||||||
import (
|
|
||||||
"budget/handler"
|
|
||||||
"budget/internal/jointaccount"
|
|
||||||
"budget/internal/storage/dao/postgresql"
|
|
||||||
"budget/internal/storage/model"
|
|
||||||
"budget/internal/storage/validators"
|
|
||||||
"budget/internal/utils"
|
|
||||||
"encoding/csv"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Context struct {
|
|
||||||
service *Service
|
|
||||||
db *Database
|
|
||||||
jointaccountService *jointaccount.Service
|
|
||||||
*handler.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) ImportJointexpenseFromCSV(gc *gin.Context) {
|
|
||||||
}
|
|
||||||
func (c *Context) CreateAnJointexpense(gc *gin.Context) {
|
|
||||||
jointaccountID := gc.Param("jointaccountId")
|
|
||||||
userId := gc.Param("userId")
|
|
||||||
csvHeaderFile, err := gc.FormFile("attachment")
|
|
||||||
if err != nil {
|
|
||||||
utils.GetLogger().Info(err)
|
|
||||||
utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
csvFile, err := csvHeaderFile.Open()
|
|
||||||
if err != nil {
|
|
||||||
utils.GetLogger().Info(err)
|
|
||||||
utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
csvr := csv.NewReader(csvFile)
|
|
||||||
csvr.FieldsPerRecord = -1
|
|
||||||
csvr.Comma = ';'
|
|
||||||
filedata, err := csvr.ReadAll()
|
|
||||||
|
|
||||||
jointaccount, err := c.jointaccountService.GetASpecificJointaccountForUser(userId, jointaccountID)
|
|
||||||
if err != nil {
|
|
||||||
utils.GetLogger().Info(err)
|
|
||||||
utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = c.service.ProcessCSVFile(filedata, jointaccount)
|
|
||||||
if err != nil {
|
|
||||||
utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var jointexpense Jointexpense
|
|
||||||
var jointexpenseEditable JointexpenseEditable
|
|
||||||
if err := gc.BindJSON(&jointexpenseEditable); err != nil {
|
|
||||||
utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
jointexpense = Jointexpense{JointexpenseEditable: jointexpenseEditable, JointaccountId: jointaccountID}
|
|
||||||
err = c.service.CreateJointexpense(&jointexpense)
|
|
||||||
if err != nil {
|
|
||||||
utils.GetLogger().Info(err)
|
|
||||||
utils.JSONErrorWithMessage(gc.Writer, model.ErrInternalServer, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
utils.JSON(gc.Writer, http.StatusCreated, jointexpense)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) DeleteJointexpense(gc *gin.Context) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) GetAllJointexpenses(gc *gin.Context) {
|
|
||||||
jointaccountId := gc.Param("jointaccountId")
|
|
||||||
from := gc.Query("from")
|
|
||||||
to := gc.Query("to")
|
|
||||||
var jointexpenses []*Jointexpense
|
|
||||||
var err error
|
|
||||||
if from != "" || to != "" {
|
|
||||||
if to == "" {
|
|
||||||
fromParsed, err := time.Parse("2006-01-02", from)
|
|
||||||
if err == nil {
|
|
||||||
to = time.Now().Format("2006-01-02")
|
|
||||||
} else {
|
|
||||||
to = fromParsed.AddDate(0, 1, 0).Format("2006-01-02")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if from == "" {
|
|
||||||
toParsed, err := time.Parse("2006-01-02", to)
|
|
||||||
if err == nil {
|
|
||||||
from = "1900-01-01"
|
|
||||||
} else {
|
|
||||||
from = toParsed.AddDate(0, -1, 0).Format("2006-01-02")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jointexpenses, err = c.service.GetJointexpensesOfAnJointaccountBetween(jointaccountId, from, to)
|
|
||||||
} else {
|
|
||||||
jointexpenses, err = c.service.GetAllJointexpensesOfAnJointaccount(jointaccountId)
|
|
||||||
}
|
|
||||||
if e, ok := err.(*model.APIError); ok {
|
|
||||||
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetAllJointexpenses: get jointexpenses")
|
|
||||||
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
|
|
||||||
} else if err != nil {
|
|
||||||
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get jointexpenses")
|
|
||||||
utils.JSONError(gc.Writer, model.ErrInternalServer)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(jointexpenses) == 0 {
|
|
||||||
utils.JSON(gc.Writer, http.StatusNoContent, nil)
|
|
||||||
} else {
|
|
||||||
utils.JSON(gc.Writer, http.StatusOK, jointexpenses)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) GetAnJointexpenses(context *gin.Context) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHandler(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Context {
|
|
||||||
database := NewDatabase(db)
|
|
||||||
service := NewService(database)
|
|
||||||
jointaccountService := jointaccount.NewService(jointaccount.NewDatabase(db))
|
|
||||||
return &Context{service: service, db: database, jointaccountService: jointaccountService, Context: ctx}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package jointexpense
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
type Jointexpense struct {
|
|
||||||
JointexpenseEditable
|
|
||||||
JointaccountId string `json:"jointaccountId,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JointexpenseEditable struct {
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
Value float32 `json:"value"`
|
|
||||||
Libelle string `json:"libelle"`
|
|
||||||
TypeJointexpense string `json:"typeJointexpense"`
|
|
||||||
JointexpenseDate time.Time `json:"jointexpenseDate,omitempty"`
|
|
||||||
CreatedAt *time.Time `json:"createdAt,omitempty"`
|
|
||||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
package jointexpense
|
|
||||||
|
|
||||||
import (
|
|
||||||
"budget/internal/jointaccount"
|
|
||||||
"budget/internal/storage/dao"
|
|
||||||
"budget/internal/storage/model"
|
|
||||||
"budget/internal/utils"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Service struct {
|
|
||||||
db *Database
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Service) GetJointexpensesOfAnJointaccountBetween(jointaccountId, from, to string) ([]*Jointexpense, error) {
|
|
||||||
jointexpenses, err := s.db.GetJointexpensesOfAnJointaccountBetween(jointaccountId, from, to)
|
|
||||||
utils.GetLogger().Info(err)
|
|
||||||
if e, ok := err.(*dao.Error); ok {
|
|
||||||
switch {
|
|
||||||
case e.Type == dao.ErrTypeNotFound:
|
|
||||||
return nil, &model.ErrNotFound
|
|
||||||
default:
|
|
||||||
return nil, &model.ErrInternalServer
|
|
||||||
}
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, &model.ErrInternalServer
|
|
||||||
}
|
|
||||||
|
|
||||||
if jointexpenses == nil {
|
|
||||||
return nil, &model.ErrNotFound
|
|
||||||
}
|
|
||||||
return jointexpenses, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Service) GetAllJointexpensesOfAnJointaccount(jointaccountId string) ([]*Jointexpense, error) {
|
|
||||||
jointexpenses, err := s.db.GetAllJointexpensesOfAnJointaccount(jointaccountId)
|
|
||||||
utils.GetLogger().Info(err)
|
|
||||||
if e, ok := err.(*dao.Error); ok {
|
|
||||||
switch {
|
|
||||||
case e.Type == dao.ErrTypeNotFound:
|
|
||||||
return nil, &model.ErrNotFound
|
|
||||||
default:
|
|
||||||
return nil, &model.ErrInternalServer
|
|
||||||
}
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, &model.ErrInternalServer
|
|
||||||
}
|
|
||||||
|
|
||||||
if jointexpenses == nil {
|
|
||||||
return nil, &model.ErrNotFound
|
|
||||||
}
|
|
||||||
return jointexpenses, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Service) CreateJointexpense(jointexpense *Jointexpense) error {
|
|
||||||
|
|
||||||
return s.db.CreateJointexpense(jointexpense)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Service) ProcessCSVFile(filedata [][]string, jointaccount *jointaccount.Jointaccount) error {
|
|
||||||
switch jointaccount.Provider {
|
|
||||||
case "caisse-epargne":
|
|
||||||
return s.processCaisseEpargne(filedata, jointaccount)
|
|
||||||
case "boursorama":
|
|
||||||
return s.processBoursorama(filedata, jointaccount)
|
|
||||||
case "bnp":
|
|
||||||
return s.processBnp(filedata, jointaccount)
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Service) processCaisseEpargne(filedata [][]string, jointaccount *jointaccount.Jointaccount) error {
|
|
||||||
for _, val := range filedata[4:] {
|
|
||||||
jointexpenseDate, err := time.Parse("02/01/06", val[0])
|
|
||||||
if err != nil {
|
|
||||||
utils.GetLogger().Info(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
amount := val[3]
|
|
||||||
typeJointexpense := "D"
|
|
||||||
if amount == "" {
|
|
||||||
amount = val[4]
|
|
||||||
typeJointexpense = "C"
|
|
||||||
}
|
|
||||||
amountParsed, err := strconv.ParseFloat(strings.Trim(strings.ReplaceAll(amount, ",", "."), "+"), 32)
|
|
||||||
if err != nil {
|
|
||||||
utils.GetLogger().Info(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
jointexpense := &Jointexpense{
|
|
||||||
JointexpenseEditable: JointexpenseEditable{
|
|
||||||
Value: float32(amountParsed),
|
|
||||||
Libelle: val[2],
|
|
||||||
TypeJointexpense: typeJointexpense,
|
|
||||||
JointexpenseDate: jointexpenseDate,
|
|
||||||
},
|
|
||||||
JointaccountId: jointaccount.ID,
|
|
||||||
}
|
|
||||||
s.CreateJointexpense(jointexpense)
|
|
||||||
utils.GetLogger().Info(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Service) processBoursorama(filedata [][]string, jointaccount *jointaccount.Jointaccount) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s Service) processBnp(filedata [][]string, jointaccount *jointaccount.Jointaccount) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewService(database *Database) *Service {
|
|
||||||
return &Service{db: database}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package sharedaccount
|
package sharedaccount
|
||||||
|
|
||||||
import "budget/internal/storage/dao/postgresql"
|
import "gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao/postgresql"
|
||||||
|
|
||||||
type Database struct {
|
type Database struct {
|
||||||
*postgresql.DatabasePostgreSQL
|
*postgresql.DatabasePostgreSQL
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package sharedaccount
|
package sharedaccount
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/handler"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/handler"
|
||||||
"budget/internal/storage/dao/postgresql"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao/postgresql"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
package postgresql
|
package postgresql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/internal/storage/dao"
|
|
||||||
"budget/internal/utils"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"
|
||||||
|
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package validators
|
package validators
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/internal/storage/model"
|
|
||||||
"budget/internal/utils"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/model"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"
|
||||||
|
|
||||||
"gopkg.in/go-playground/validator.v9"
|
"gopkg.in/go-playground/validator.v9"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/internal/storage/dao"
|
|
||||||
"budget/internal/storage/dao/postgresql"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao/postgresql"
|
||||||
|
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,7 +26,12 @@ func (db *Database) GetAllUsers() ([]*User, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer func(rows *sql.Rows) {
|
||||||
|
err := rows.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(rows)
|
||||||
|
|
||||||
us := make([]*User, 0)
|
us := make([]*User, 0)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/handler"
|
|
||||||
"budget/internal/storage/dao/postgresql"
|
|
||||||
"budget/internal/storage/model"
|
|
||||||
"budget/internal/utils"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/handler"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao/postgresql"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/model"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"google.golang.org/api/oauth2/v2"
|
"google.golang.org/api/oauth2/v2"
|
||||||
)
|
)
|
||||||
@@ -42,7 +43,7 @@ func (hc *Context) ConnectUser(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
oauth2Service, err := oauth2.New(&http.Client{})
|
oauth2Service, err := oauth2.NewService(c)
|
||||||
if oauth2Service == nil {
|
if oauth2Service == nil {
|
||||||
utils.GetLoggerFromCtx(c).WithError(err).Error(err)
|
utils.GetLoggerFromCtx(c).WithError(err).Error(err)
|
||||||
utils.JSONError(c.Writer, model.ErrInternalServer)
|
utils.JSONError(c.Writer, model.ErrInternalServer)
|
||||||
@@ -87,7 +88,7 @@ func (hc *Context) CreateUser(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
oauth2Service, err := oauth2.New(&http.Client{})
|
oauth2Service, err := oauth2.NewService(c)
|
||||||
if oauth2Service == nil {
|
if oauth2Service == nil {
|
||||||
utils.GetLogger().WithError(err).Error(err)
|
utils.GetLogger().WithError(err).Error(err)
|
||||||
utils.JSONError(c.Writer, model.ErrInternalServer)
|
utils.JSONError(c.Writer, model.ErrInternalServer)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package user
|
package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/internal/storage/dao"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao"
|
||||||
"budget/internal/storage/model"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
@@ -16,8 +16,8 @@ func NewService(database *Database) *Service {
|
|||||||
func (s *Service) GetUserById(userId string) (*User, error) {
|
func (s *Service) GetUserById(userId string) (*User, error) {
|
||||||
user, err := s.db.GetUsersByID(userId)
|
user, err := s.db.GetUsersByID(userId)
|
||||||
if e, ok := err.(*dao.Error); ok {
|
if e, ok := err.(*dao.Error); ok {
|
||||||
switch {
|
switch e.Type {
|
||||||
case e.Type == dao.ErrTypeNotFound:
|
case dao.ErrTypeNotFound:
|
||||||
return nil, &model.ErrNotFound
|
return nil, &model.ErrNotFound
|
||||||
default:
|
default:
|
||||||
return nil, &model.ErrInternalServer
|
return nil, &model.ErrInternalServer
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/internal/storage/model"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func JSON(w http.ResponseWriter, status int, data interface{}) {
|
func JSON(w http.ResponseWriter, status int, data interface{}) {
|
||||||
w.Header().Set(HeaderNameContentType, HeaderValueApplicationJSONUTF8)
|
w.Header().Set(HeaderNameContentType, HeaderValueApplicationJSONUTF8)
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
if data != nil {
|
if data != nil {
|
||||||
json.NewEncoder(w).Encode(data)
|
err := json.NewEncoder(w).Encode(data)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
main.go
7
main.go
@@ -1,14 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/cmd"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/cmd"
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
rand.Seed(time.Now().UTC().UnixNano())
|
|
||||||
}
|
|
||||||
func main() {
|
func main() {
|
||||||
cmd.Execute()
|
cmd.Execute()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/internal/storage/model"
|
|
||||||
"budget/internal/utils"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/model"
|
||||||
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"google.golang.org/api/oauth2/v1"
|
"google.golang.org/api/oauth2/v1"
|
||||||
)
|
)
|
||||||
@@ -19,7 +19,7 @@ func ValidateOAuthToken(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
oauth2Service, err := oauth2.New(&http.Client{})
|
oauth2Service, err := oauth2.NewService(c)
|
||||||
if oauth2Service == nil {
|
if oauth2Service == nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
utils.JSONError(c.Writer, model.ErrInternalServer)
|
utils.JSONError(c.Writer, model.ErrInternalServer)
|
||||||
|
|||||||
@@ -1,27 +1,25 @@
|
|||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"budget/handler"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/handler"
|
||||||
"budget/internal/account"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/account"
|
||||||
"budget/internal/jointaccount"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/dao/postgresql"
|
||||||
"budget/internal/storage/dao/postgresql"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/model"
|
||||||
"budget/internal/storage/model"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/storage/validators"
|
||||||
"budget/internal/storage/validators"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/user"
|
||||||
"budget/internal/user"
|
"gitea.frenchtouch.duckdns.org/kratisto/budget-backend/internal/utils"
|
||||||
"budget/internal/utils"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Validator struct {
|
type Validator struct {
|
||||||
jointaccountService *jointaccount.Service
|
|
||||||
accountService *account.Service
|
accountService *account.Service
|
||||||
userService *user.Service
|
userService *user.Service
|
||||||
*handler.Context
|
*handler.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewValidator(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Validator {
|
func NewValidator(ctx *handler.Context, db *postgresql.DatabasePostgreSQL) *Validator {
|
||||||
return &Validator{accountService: account.NewService(account.NewDatabase(db)), userService: user.NewService(user.NewDatabase(db)), jointaccountService: jointaccount.NewService(jointaccount.NewDatabase(db)), Context: ctx}
|
return &Validator{accountService: account.NewService(account.NewDatabase(db)), userService: user.NewService(user.NewDatabase(db)), Context: ctx}
|
||||||
}
|
}
|
||||||
func (v Validator) HasValidUserId(gc *gin.Context) {
|
func (v Validator) HasValidUserId(gc *gin.Context) {
|
||||||
userId := gc.Param("userId")
|
userId := gc.Param("userId")
|
||||||
@@ -62,7 +60,7 @@ func (v Validator) UserdIdMatchOAuthToken(gc *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
googleUserId, exists := gc.Get("googleUserId")
|
googleUserId, exists := gc.Get("googleUserId")
|
||||||
if exists == false {
|
if !exists {
|
||||||
utils.GetLoggerFromCtx(gc).Error("error while getting google user id")
|
utils.GetLoggerFromCtx(gc).Error("error while getting google user id")
|
||||||
utils.JSONError(gc.Writer, model.ErrInternalServer)
|
utils.JSONError(gc.Writer, model.ErrInternalServer)
|
||||||
return
|
return
|
||||||
@@ -110,28 +108,3 @@ func (v Validator) AccountExists(gc *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Validator) HasValidJointAccountId(gc *gin.Context) {
|
|
||||||
accountId := gc.Param("jointaccountId")
|
|
||||||
err := v.Validator.VarCtx(gc, accountId, "uuid4")
|
|
||||||
if err != nil {
|
|
||||||
utils.JSONError(gc.Writer, validators.NewDataValidationAPIError(err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v Validator) JointAccountExists(gc *gin.Context) {
|
|
||||||
userId := gc.Param("userId")
|
|
||||||
accountId := gc.Param("jointaccountId")
|
|
||||||
_, err := v.jointaccountService.GetASpecificJointaccountForUser(userId, accountId)
|
|
||||||
if e, ok := err.(*model.APIError); ok {
|
|
||||||
utils.GetLogger().Info(err)
|
|
||||||
utils.GetLoggerFromCtx(gc).WithError(err).WithField("type", e.Type).Error("error GetUserFromGoogleID: get user from google user id")
|
|
||||||
utils.JSONErrorWithMessage(gc.Writer, *e, e.Description)
|
|
||||||
return
|
|
||||||
} else if err != nil {
|
|
||||||
utils.GetLoggerFromCtx(gc).WithError(err).Error("error while get user from google user id")
|
|
||||||
utils.JSONError(gc.Writer, model.ErrInternalServer)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
469
vendor/cloud.google.com/go/auth/CHANGES.md
generated
vendored
Normal file
469
vendor/cloud.google.com/go/auth/CHANGES.md
generated
vendored
Normal file
@@ -0,0 +1,469 @@
|
|||||||
|
# Changes
|
||||||
|
|
||||||
|
## [0.18.1](https://github.com/googleapis/google-cloud-go/releases/tag/auth%2Fv0.18.1) (2026-01-21)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add InternalOptions.TelemetryAttributes for internal client use (#13641) ([3876978](https://github.com/googleapis/google-cloud-go/commit/38769789755ed47d85e85dcd56596109de65f780))
|
||||||
|
* remove singleton and restore normal usage of otelgrpc.clientHandler (#13522) ([673d4b0](https://github.com/googleapis/google-cloud-go/commit/673d4b05617f833aa433f7f6a350b5cb888ea20d))
|
||||||
|
|
||||||
|
## [0.18.0](https://github.com/googleapis/google-cloud-go/releases/tag/auth%2Fv0.18.0) (2025-12-15)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Support scopes field from impersonated credential json (#13308) ([e3f62e1](https://github.com/googleapis/google-cloud-go/commit/e3f62e102840127a0058f5cced4c9738f2bf45f2))
|
||||||
|
* add support for parsing EC private key (#13317) ([ea6bc62](https://github.com/googleapis/google-cloud-go/commit/ea6bc62ffe2cc0a6d607d698a181b37fa46c340d))
|
||||||
|
* deprecate unsafe credentials JSON loading options (#13397) ([0dd2a3b](https://github.com/googleapis/google-cloud-go/commit/0dd2a3bdece9a85ee7216a737559fa9f5a869545))
|
||||||
|
|
||||||
|
## [0.17.0](https://github.com/googleapis/google-cloud-go/releases/tag/auth%2Fv0.17.0) (2025-10-02)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add trust boundary support for service accounts and impersonation (HTTP/gRPC) (#11870) ([5c2b665](https://github.com/googleapis/google-cloud-go/commit/5c2b665f392e6dd90192f107188720aa1357e7da))
|
||||||
|
* add trust boundary support for external accounts (#12864) ([a67a146](https://github.com/googleapis/google-cloud-go/commit/a67a146a6a88a6f1ba10c409dfce8015ecd60a64))
|
||||||
|
|
||||||
|
## [0.16.5](https://github.com/googleapis/google-cloud-go/compare/auth/v0.16.4...auth/v0.16.5) (2025-08-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Improve error message for unknown credentials type ([#12673](https://github.com/googleapis/google-cloud-go/issues/12673)) ([558b164](https://github.com/googleapis/google-cloud-go/commit/558b16429f621276694405fa5f2091199f2d4c4d))
|
||||||
|
* **auth:** Set Content-Type in userTokenProvider.exchangeToken ([#12634](https://github.com/googleapis/google-cloud-go/issues/12634)) ([1197ebc](https://github.com/googleapis/google-cloud-go/commit/1197ebcbca491f8c610da732c7361c90bc6f46d0))
|
||||||
|
|
||||||
|
## [0.16.4](https://github.com/googleapis/google-cloud-go/compare/auth/v0.16.3...auth/v0.16.4) (2025-08-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Add UseDefaultClient: true to metadata.Options ([#12666](https://github.com/googleapis/google-cloud-go/issues/12666)) ([1482191](https://github.com/googleapis/google-cloud-go/commit/1482191e88236693efef68769752638281566766)), refs [#11078](https://github.com/googleapis/google-cloud-go/issues/11078) [#12657](https://github.com/googleapis/google-cloud-go/issues/12657)
|
||||||
|
|
||||||
|
## [0.16.3](https://github.com/googleapis/google-cloud-go/compare/auth/v0.16.2...auth/v0.16.3) (2025-07-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Fix race condition in cachedTokenProvider.tokenAsync ([#12586](https://github.com/googleapis/google-cloud-go/issues/12586)) ([73867cc](https://github.com/googleapis/google-cloud-go/commit/73867ccc1e9808d65361bcfc0776bd95fe34dbb3))
|
||||||
|
|
||||||
|
## [0.16.2](https://github.com/googleapis/google-cloud-go/compare/auth/v0.16.1...auth/v0.16.2) (2025-06-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Add back DirectPath misconfiguration logging ([#11162](https://github.com/googleapis/google-cloud-go/issues/11162)) ([8d52da5](https://github.com/googleapis/google-cloud-go/commit/8d52da58da5a0ed77a0f6307d1b561bc045406a1))
|
||||||
|
* **auth:** Remove s2a fallback option ([#12354](https://github.com/googleapis/google-cloud-go/issues/12354)) ([d5acc59](https://github.com/googleapis/google-cloud-go/commit/d5acc599cd775ddc404349e75906fa02e8ff133e))
|
||||||
|
|
||||||
|
## [0.16.1](https://github.com/googleapis/google-cloud-go/compare/auth/v0.16.0...auth/v0.16.1) (2025-04-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Clone detectopts before assigning TokenBindingType ([#11881](https://github.com/googleapis/google-cloud-go/issues/11881)) ([2167b02](https://github.com/googleapis/google-cloud-go/commit/2167b020fdc43b517c2b6ecca264a10e357ea035))
|
||||||
|
|
||||||
|
## [0.16.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.15.0...auth/v0.16.0) (2025-04-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth/credentials:** Return X.509 certificate chain as subject token ([#11948](https://github.com/googleapis/google-cloud-go/issues/11948)) ([d445a3f](https://github.com/googleapis/google-cloud-go/commit/d445a3f66272ffd5c39c4939af9bebad4582631c)), refs [#11757](https://github.com/googleapis/google-cloud-go/issues/11757)
|
||||||
|
* **auth:** Configure DirectPath bound credentials from AllowedHardBoundTokens ([#11665](https://github.com/googleapis/google-cloud-go/issues/11665)) ([0fc40bc](https://github.com/googleapis/google-cloud-go/commit/0fc40bcf4e4673704df0973e9fa65957395d7bb4))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Allow non-default SA credentials for DP ([#11828](https://github.com/googleapis/google-cloud-go/issues/11828)) ([3a996b4](https://github.com/googleapis/google-cloud-go/commit/3a996b4129e6d0a34dfda6671f535d5aefb26a82))
|
||||||
|
* **auth:** Restore calling DialContext ([#11930](https://github.com/googleapis/google-cloud-go/issues/11930)) ([9ec9a29](https://github.com/googleapis/google-cloud-go/commit/9ec9a29494e93197edbaf45aba28984801e9770a)), refs [#11118](https://github.com/googleapis/google-cloud-go/issues/11118)
|
||||||
|
|
||||||
|
## [0.15.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.14.1...auth/v0.15.0) (2025-02-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** Add hard-bound token request to compute token provider. ([#11588](https://github.com/googleapis/google-cloud-go/issues/11588)) ([0e608bb](https://github.com/googleapis/google-cloud-go/commit/0e608bb5ac3d694c8ad36ca4340071d3a2c78699))
|
||||||
|
|
||||||
|
## [0.14.1](https://github.com/googleapis/google-cloud-go/compare/auth/v0.14.0...auth/v0.14.1) (2025-01-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
* **auth:** Add warning about externally-provided credentials ([#11462](https://github.com/googleapis/google-cloud-go/issues/11462)) ([49fb6ff](https://github.com/googleapis/google-cloud-go/commit/49fb6ff4d754895f82c9c4d502fc7547d3b5a941))
|
||||||
|
|
||||||
|
## [0.14.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.13.0...auth/v0.14.0) (2025-01-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** Add universe domain support to idtoken ([#11059](https://github.com/googleapis/google-cloud-go/issues/11059)) ([72add7e](https://github.com/googleapis/google-cloud-go/commit/72add7e9f8f455af695e8ef79212a4bd3122fb3a))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Update golang.org/x/net to v0.33.0 ([e9b0b69](https://github.com/googleapis/google-cloud-go/commit/e9b0b69644ea5b276cacff0a707e8a5e87efafc9))
|
||||||
|
* **auth:** Fix copy of delegates in impersonate.NewIDTokenCredentials ([#11386](https://github.com/googleapis/google-cloud-go/issues/11386)) ([ff7ef8e](https://github.com/googleapis/google-cloud-go/commit/ff7ef8e7ade7171bce3e4f30ff10a2e9f6c27ca0)), refs [#11379](https://github.com/googleapis/google-cloud-go/issues/11379)
|
||||||
|
* **auth:** Update golang.org/x/net to v0.33.0 ([e9b0b69](https://github.com/googleapis/google-cloud-go/commit/e9b0b69644ea5b276cacff0a707e8a5e87efafc9))
|
||||||
|
|
||||||
|
## [0.13.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.12.1...auth/v0.13.0) (2024-12-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** Add logging support ([#11079](https://github.com/googleapis/google-cloud-go/issues/11079)) ([c80e31d](https://github.com/googleapis/google-cloud-go/commit/c80e31df5ecb33a810be3dfb9d9e27ac531aa91d))
|
||||||
|
* **auth:** Pass logger from auth layer to metadata package ([#11288](https://github.com/googleapis/google-cloud-go/issues/11288)) ([b552efd](https://github.com/googleapis/google-cloud-go/commit/b552efd6ab34e5dfded18438e0fbfd925805614f))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Check compute cred type before non-default flag for DP ([#11255](https://github.com/googleapis/google-cloud-go/issues/11255)) ([4347ca1](https://github.com/googleapis/google-cloud-go/commit/4347ca141892be8ae813399b4b437662a103bc90))
|
||||||
|
|
||||||
|
## [0.12.1](https://github.com/googleapis/google-cloud-go/compare/auth/v0.12.0...auth/v0.12.1) (2024-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Correct typo in link ([#11160](https://github.com/googleapis/google-cloud-go/issues/11160)) ([af6fb46](https://github.com/googleapis/google-cloud-go/commit/af6fb46d7cd694ddbe8c9d63bc4cdcd62b9fb2c1))
|
||||||
|
|
||||||
|
## [0.12.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.11.0...auth/v0.12.0) (2024-12-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** Add support for providing custom certificate URL ([#11006](https://github.com/googleapis/google-cloud-go/issues/11006)) ([ebf3657](https://github.com/googleapis/google-cloud-go/commit/ebf36579724afb375d3974cf1da38f703e3b7dbc)), refs [#11005](https://github.com/googleapis/google-cloud-go/issues/11005)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Ensure endpoints are present in Validator ([#11209](https://github.com/googleapis/google-cloud-go/issues/11209)) ([106cd53](https://github.com/googleapis/google-cloud-go/commit/106cd53309facaef1b8ea78376179f523f6912b9)), refs [#11006](https://github.com/googleapis/google-cloud-go/issues/11006) [#11190](https://github.com/googleapis/google-cloud-go/issues/11190) [#11189](https://github.com/googleapis/google-cloud-go/issues/11189) [#11188](https://github.com/googleapis/google-cloud-go/issues/11188)
|
||||||
|
|
||||||
|
## [0.11.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.10.2...auth/v0.11.0) (2024-11-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** Add universe domain support to mTLS ([#11159](https://github.com/googleapis/google-cloud-go/issues/11159)) ([117748b](https://github.com/googleapis/google-cloud-go/commit/117748ba1cfd4ae62a6a4feb7e30951cb2bc9344))
|
||||||
|
|
||||||
|
## [0.10.2](https://github.com/googleapis/google-cloud-go/compare/auth/v0.10.1...auth/v0.10.2) (2024-11-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Restore use of grpc.Dial ([#11118](https://github.com/googleapis/google-cloud-go/issues/11118)) ([2456b94](https://github.com/googleapis/google-cloud-go/commit/2456b943b7b8aaabd4d8bfb7572c0f477ae0db45)), refs [#7556](https://github.com/googleapis/google-cloud-go/issues/7556)
|
||||||
|
|
||||||
|
## [0.10.1](https://github.com/googleapis/google-cloud-go/compare/auth/v0.10.0...auth/v0.10.1) (2024-11-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Restore Application Default Credentials support to idtoken ([#11083](https://github.com/googleapis/google-cloud-go/issues/11083)) ([8771f2e](https://github.com/googleapis/google-cloud-go/commit/8771f2ea9807ab822083808e0678392edff3b4f2))
|
||||||
|
* **auth:** Skip impersonate universe domain check if empty ([#11086](https://github.com/googleapis/google-cloud-go/issues/11086)) ([87159c1](https://github.com/googleapis/google-cloud-go/commit/87159c1059d4a18d1367ce62746a838a94964ab6))
|
||||||
|
|
||||||
|
## [0.10.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.9.9...auth/v0.10.0) (2024-10-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** Add universe domain support to credentials/impersonate ([#10953](https://github.com/googleapis/google-cloud-go/issues/10953)) ([e06cb64](https://github.com/googleapis/google-cloud-go/commit/e06cb6499f7eda3aef08ab18ff197016f667684b))
|
||||||
|
|
||||||
|
## [0.9.9](https://github.com/googleapis/google-cloud-go/compare/auth/v0.9.8...auth/v0.9.9) (2024-10-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Fallback cert lookups for missing files ([#11013](https://github.com/googleapis/google-cloud-go/issues/11013)) ([bd76695](https://github.com/googleapis/google-cloud-go/commit/bd766957ec238b7c40ddbabb369e612dc9b07313)), refs [#10844](https://github.com/googleapis/google-cloud-go/issues/10844)
|
||||||
|
* **auth:** Replace MDS endpoint universe_domain with universe-domain ([#11000](https://github.com/googleapis/google-cloud-go/issues/11000)) ([6a1586f](https://github.com/googleapis/google-cloud-go/commit/6a1586f2ce9974684affaea84e7b629313b4d114))
|
||||||
|
|
||||||
|
## [0.9.8](https://github.com/googleapis/google-cloud-go/compare/auth/v0.9.7...auth/v0.9.8) (2024-10-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Restore OpenTelemetry handling in transports ([#10968](https://github.com/googleapis/google-cloud-go/issues/10968)) ([08c6d04](https://github.com/googleapis/google-cloud-go/commit/08c6d04901c1a20e219b2d86df41dbaa6d7d7b55)), refs [#10962](https://github.com/googleapis/google-cloud-go/issues/10962)
|
||||||
|
* **auth:** Try talk to plaintext S2A if credentials can not be found for mTLS-S2A ([#10941](https://github.com/googleapis/google-cloud-go/issues/10941)) ([0f0bf2d](https://github.com/googleapis/google-cloud-go/commit/0f0bf2d18c97dd8b65bcf0099f0802b5631c6287))
|
||||||
|
|
||||||
|
## [0.9.7](https://github.com/googleapis/google-cloud-go/compare/auth/v0.9.6...auth/v0.9.7) (2024-10-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Restore support for non-default service accounts for DirectPath ([#10937](https://github.com/googleapis/google-cloud-go/issues/10937)) ([a38650e](https://github.com/googleapis/google-cloud-go/commit/a38650edbf420223077498cafa537aec74b37aad)), refs [#10907](https://github.com/googleapis/google-cloud-go/issues/10907)
|
||||||
|
|
||||||
|
## [0.9.6](https://github.com/googleapis/google-cloud-go/compare/auth/v0.9.5...auth/v0.9.6) (2024-09-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Make aws credentials provider retrieve fresh credentials ([#10920](https://github.com/googleapis/google-cloud-go/issues/10920)) ([250fbf8](https://github.com/googleapis/google-cloud-go/commit/250fbf87d858d865e399a241b7e537c4ff0c3dd8))
|
||||||
|
|
||||||
|
## [0.9.5](https://github.com/googleapis/google-cloud-go/compare/auth/v0.9.4...auth/v0.9.5) (2024-09-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Restore support for GOOGLE_CLOUD_UNIVERSE_DOMAIN env ([#10915](https://github.com/googleapis/google-cloud-go/issues/10915)) ([94caaaa](https://github.com/googleapis/google-cloud-go/commit/94caaaa061362d0e00ef6214afcc8a0a3e7ebfb2))
|
||||||
|
* **auth:** Skip directpath credentials overwrite when it's not on GCE ([#10833](https://github.com/googleapis/google-cloud-go/issues/10833)) ([7e5e8d1](https://github.com/googleapis/google-cloud-go/commit/7e5e8d10b761b0a6e43e19a028528db361bc07b1))
|
||||||
|
* **auth:** Use new context for non-blocking token refresh ([#10919](https://github.com/googleapis/google-cloud-go/issues/10919)) ([cf7102d](https://github.com/googleapis/google-cloud-go/commit/cf7102d33a21be1e5a9d47a49456b3a57c43b350))
|
||||||
|
|
||||||
|
## [0.9.4](https://github.com/googleapis/google-cloud-go/compare/auth/v0.9.3...auth/v0.9.4) (2024-09-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Enable self-signed JWT for non-GDU universe domain ([#10831](https://github.com/googleapis/google-cloud-go/issues/10831)) ([f9869f7](https://github.com/googleapis/google-cloud-go/commit/f9869f7903cfd34d1b97c25d0dc5669d2c5138e6))
|
||||||
|
|
||||||
|
## [0.9.3](https://github.com/googleapis/google-cloud-go/compare/auth/v0.9.2...auth/v0.9.3) (2024-09-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Choose quota project envvar over file when both present ([#10807](https://github.com/googleapis/google-cloud-go/issues/10807)) ([2d8dd77](https://github.com/googleapis/google-cloud-go/commit/2d8dd7700eff92d4b95027be55e26e1e7aa79181)), refs [#10804](https://github.com/googleapis/google-cloud-go/issues/10804)
|
||||||
|
|
||||||
|
## [0.9.2](https://github.com/googleapis/google-cloud-go/compare/auth/v0.9.1...auth/v0.9.2) (2024-08-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Handle non-Transport DefaultTransport ([#10733](https://github.com/googleapis/google-cloud-go/issues/10733)) ([98d91dc](https://github.com/googleapis/google-cloud-go/commit/98d91dc8316b247498fab41ab35e57a0446fe556)), refs [#10742](https://github.com/googleapis/google-cloud-go/issues/10742)
|
||||||
|
* **auth:** Make sure quota option takes precedence over env/file ([#10797](https://github.com/googleapis/google-cloud-go/issues/10797)) ([f1b050d](https://github.com/googleapis/google-cloud-go/commit/f1b050d56d804b245cab048c2980d32b0eaceb4e)), refs [#10795](https://github.com/googleapis/google-cloud-go/issues/10795)
|
||||||
|
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
* **auth:** Fix Go doc comment link ([#10751](https://github.com/googleapis/google-cloud-go/issues/10751)) ([015acfa](https://github.com/googleapis/google-cloud-go/commit/015acfab4d172650928bb1119bc2cd6307b9a437))
|
||||||
|
|
||||||
|
## [0.9.1](https://github.com/googleapis/google-cloud-go/compare/auth/v0.9.0...auth/v0.9.1) (2024-08-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Setting expireEarly to default when the value is 0 ([#10732](https://github.com/googleapis/google-cloud-go/issues/10732)) ([5e67869](https://github.com/googleapis/google-cloud-go/commit/5e67869a31e9e8ecb4eeebd2cfa11a761c3b1948))
|
||||||
|
|
||||||
|
## [0.9.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.8.1...auth/v0.9.0) (2024-08-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** Auth library can talk to S2A over mTLS ([#10634](https://github.com/googleapis/google-cloud-go/issues/10634)) ([5250a13](https://github.com/googleapis/google-cloud-go/commit/5250a13ec95b8d4eefbe0158f82857ff2189cb45))
|
||||||
|
|
||||||
|
## [0.8.1](https://github.com/googleapis/google-cloud-go/compare/auth/v0.8.0...auth/v0.8.1) (2024-08-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Make default client creation more lenient ([#10669](https://github.com/googleapis/google-cloud-go/issues/10669)) ([1afb9ee](https://github.com/googleapis/google-cloud-go/commit/1afb9ee1ee9de9810722800018133304a0ca34d1)), refs [#10638](https://github.com/googleapis/google-cloud-go/issues/10638)
|
||||||
|
|
||||||
|
## [0.8.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.7.3...auth/v0.8.0) (2024-08-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** Adds support for X509 workload identity federation ([#10373](https://github.com/googleapis/google-cloud-go/issues/10373)) ([5d07505](https://github.com/googleapis/google-cloud-go/commit/5d075056cbe27bb1da4072a26070c41f8999eb9b))
|
||||||
|
|
||||||
|
## [0.7.3](https://github.com/googleapis/google-cloud-go/compare/auth/v0.7.2...auth/v0.7.3) (2024-08-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Update dependencies ([257c40b](https://github.com/googleapis/google-cloud-go/commit/257c40bd6d7e59730017cf32bda8823d7a232758))
|
||||||
|
* **auth:** Disable automatic universe domain check for MDS ([#10620](https://github.com/googleapis/google-cloud-go/issues/10620)) ([7cea5ed](https://github.com/googleapis/google-cloud-go/commit/7cea5edd5a0c1e6bca558696f5607879141910e8))
|
||||||
|
* **auth:** Update dependencies ([257c40b](https://github.com/googleapis/google-cloud-go/commit/257c40bd6d7e59730017cf32bda8823d7a232758))
|
||||||
|
|
||||||
|
## [0.7.2](https://github.com/googleapis/google-cloud-go/compare/auth/v0.7.1...auth/v0.7.2) (2024-07-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Use default client for universe metadata lookup ([#10551](https://github.com/googleapis/google-cloud-go/issues/10551)) ([d9046fd](https://github.com/googleapis/google-cloud-go/commit/d9046fdd1435d1ce48f374806c1def4cb5ac6cd3)), refs [#10544](https://github.com/googleapis/google-cloud-go/issues/10544)
|
||||||
|
|
||||||
|
## [0.7.1](https://github.com/googleapis/google-cloud-go/compare/auth/v0.7.0...auth/v0.7.1) (2024-07-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Bump google.golang.org/grpc@v1.64.1 ([8ecc4e9](https://github.com/googleapis/google-cloud-go/commit/8ecc4e9622e5bbe9b90384d5848ab816027226c5))
|
||||||
|
|
||||||
|
## [0.7.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.6.1...auth/v0.7.0) (2024-07-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** Add workload X509 cert provider as a default cert provider ([#10479](https://github.com/googleapis/google-cloud-go/issues/10479)) ([c51ee6c](https://github.com/googleapis/google-cloud-go/commit/c51ee6cf65ce05b4d501083e49d468c75ac1ea63))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Bump google.golang.org/api@v0.187.0 ([8fa9e39](https://github.com/googleapis/google-cloud-go/commit/8fa9e398e512fd8533fd49060371e61b5725a85b))
|
||||||
|
* **auth:** Bump google.golang.org/api@v0.187.0 ([8fa9e39](https://github.com/googleapis/google-cloud-go/commit/8fa9e398e512fd8533fd49060371e61b5725a85b))
|
||||||
|
* **auth:** Check len of slices, not non-nil ([#10483](https://github.com/googleapis/google-cloud-go/issues/10483)) ([0a966a1](https://github.com/googleapis/google-cloud-go/commit/0a966a183e5f0e811977216d736d875b7233e942))
|
||||||
|
|
||||||
|
## [0.6.1](https://github.com/googleapis/google-cloud-go/compare/auth/v0.6.0...auth/v0.6.1) (2024-07-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Support gRPC API keys ([#10460](https://github.com/googleapis/google-cloud-go/issues/10460)) ([daa6646](https://github.com/googleapis/google-cloud-go/commit/daa6646d2af5d7fb5b30489f4934c7db89868c7c))
|
||||||
|
* **auth:** Update http and grpc transports to support token exchange over mTLS ([#10397](https://github.com/googleapis/google-cloud-go/issues/10397)) ([c6dfdcf](https://github.com/googleapis/google-cloud-go/commit/c6dfdcf893c3f971eba15026c12db0a960ae81f2))
|
||||||
|
|
||||||
|
## [0.6.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.5.2...auth/v0.6.0) (2024-06-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** Add non-blocking token refresh for compute MDS ([#10263](https://github.com/googleapis/google-cloud-go/issues/10263)) ([9ac350d](https://github.com/googleapis/google-cloud-go/commit/9ac350da11a49b8e2174d3fc5b1a5070fec78b4e))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Return error if envvar detected file returns an error ([#10431](https://github.com/googleapis/google-cloud-go/issues/10431)) ([e52b9a7](https://github.com/googleapis/google-cloud-go/commit/e52b9a7c45468827f5d220ab00965191faeb9d05))
|
||||||
|
|
||||||
|
## [0.5.2](https://github.com/googleapis/google-cloud-go/compare/auth/v0.5.1...auth/v0.5.2) (2024-06-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Fetch initial token when CachedTokenProviderOptions.DisableAutoRefresh is true ([#10415](https://github.com/googleapis/google-cloud-go/issues/10415)) ([3266763](https://github.com/googleapis/google-cloud-go/commit/32667635ca2efad05cd8c087c004ca07d7406913)), refs [#10414](https://github.com/googleapis/google-cloud-go/issues/10414)
|
||||||
|
|
||||||
|
## [0.5.1](https://github.com/googleapis/google-cloud-go/compare/auth/v0.5.0...auth/v0.5.1) (2024-05-31)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Pass through client to 2LO and 3LO flows ([#10290](https://github.com/googleapis/google-cloud-go/issues/10290)) ([685784e](https://github.com/googleapis/google-cloud-go/commit/685784ea84358c15e9214bdecb307d37aa3b6d2f))
|
||||||
|
|
||||||
|
## [0.5.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.4.2...auth/v0.5.0) (2024-05-28)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** Adds X509 workload certificate provider ([#10233](https://github.com/googleapis/google-cloud-go/issues/10233)) ([17a9db7](https://github.com/googleapis/google-cloud-go/commit/17a9db73af35e3d1a7a25ac4fd1377a103de6150))
|
||||||
|
|
||||||
|
## [0.4.2](https://github.com/googleapis/google-cloud-go/compare/auth/v0.4.1...auth/v0.4.2) (2024-05-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Enable client certificates by default only for GDU ([#10151](https://github.com/googleapis/google-cloud-go/issues/10151)) ([7c52978](https://github.com/googleapis/google-cloud-go/commit/7c529786275a39b7e00525f7d5e7be0d963e9e15))
|
||||||
|
* **auth:** Handle non-Transport DefaultTransport ([#10162](https://github.com/googleapis/google-cloud-go/issues/10162)) ([fa3bfdb](https://github.com/googleapis/google-cloud-go/commit/fa3bfdb23aaa45b34394a8b61e753b3587506782)), refs [#10159](https://github.com/googleapis/google-cloud-go/issues/10159)
|
||||||
|
* **auth:** Have refresh time match docs ([#10147](https://github.com/googleapis/google-cloud-go/issues/10147)) ([bcb5568](https://github.com/googleapis/google-cloud-go/commit/bcb5568c07a54dd3d2e869d15f502b0741a609e8))
|
||||||
|
* **auth:** Update compute token fetching error with named prefix ([#10180](https://github.com/googleapis/google-cloud-go/issues/10180)) ([4573504](https://github.com/googleapis/google-cloud-go/commit/4573504828d2928bebedc875d87650ba227829ea))
|
||||||
|
|
||||||
|
## [0.4.1](https://github.com/googleapis/google-cloud-go/compare/auth/v0.4.0...auth/v0.4.1) (2024-05-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Don't try to detect default creds it opt configured ([#10143](https://github.com/googleapis/google-cloud-go/issues/10143)) ([804632e](https://github.com/googleapis/google-cloud-go/commit/804632e7c5b0b85ff522f7951114485e256eb5bc))
|
||||||
|
|
||||||
|
## [0.4.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.3.0...auth/v0.4.0) (2024-05-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** Enable client certificates by default ([#10102](https://github.com/googleapis/google-cloud-go/issues/10102)) ([9013e52](https://github.com/googleapis/google-cloud-go/commit/9013e5200a6ec0f178ed91acb255481ffb073a2c))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Get s2a logic up to date ([#10093](https://github.com/googleapis/google-cloud-go/issues/10093)) ([4fe9ae4](https://github.com/googleapis/google-cloud-go/commit/4fe9ae4b7101af2a5221d6d6b2e77b479305bb06))
|
||||||
|
|
||||||
|
## [0.3.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.2.2...auth/v0.3.0) (2024-04-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth/httptransport:** Add ability to customize transport ([#10023](https://github.com/googleapis/google-cloud-go/issues/10023)) ([72c7f6b](https://github.com/googleapis/google-cloud-go/commit/72c7f6bbec3136cc7a62788fc7186bc33ef6c3b3)), refs [#9812](https://github.com/googleapis/google-cloud-go/issues/9812) [#9814](https://github.com/googleapis/google-cloud-go/issues/9814)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/credentials:** Error on bad file name if explicitly set ([#10018](https://github.com/googleapis/google-cloud-go/issues/10018)) ([55beaa9](https://github.com/googleapis/google-cloud-go/commit/55beaa993aaf052d8be39766afc6777c3c2a0bdd)), refs [#9809](https://github.com/googleapis/google-cloud-go/issues/9809)
|
||||||
|
|
||||||
|
## [0.2.2](https://github.com/googleapis/google-cloud-go/compare/auth/v0.2.1...auth/v0.2.2) (2024-04-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Add internal opt to skip validation on transports ([#9999](https://github.com/googleapis/google-cloud-go/issues/9999)) ([9e20ef8](https://github.com/googleapis/google-cloud-go/commit/9e20ef89f6287d6bd03b8697d5898dc43b4a77cf)), refs [#9823](https://github.com/googleapis/google-cloud-go/issues/9823)
|
||||||
|
* **auth:** Set secure flag for gRPC conn pools ([#10002](https://github.com/googleapis/google-cloud-go/issues/10002)) ([14e3956](https://github.com/googleapis/google-cloud-go/commit/14e3956dfd736399731b5ee8d9b178ae085cf7ba)), refs [#9833](https://github.com/googleapis/google-cloud-go/issues/9833)
|
||||||
|
|
||||||
|
## [0.2.1](https://github.com/googleapis/google-cloud-go/compare/auth/v0.2.0...auth/v0.2.1) (2024-04-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth:** Default gRPC token type to Bearer if not set ([#9800](https://github.com/googleapis/google-cloud-go/issues/9800)) ([5284066](https://github.com/googleapis/google-cloud-go/commit/5284066670b6fe65d79089cfe0199c9660f87fc7))
|
||||||
|
|
||||||
|
## [0.2.0](https://github.com/googleapis/google-cloud-go/compare/auth/v0.1.1...auth/v0.2.0) (2024-04-15)
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
In the below mentioned commits there were a few large breaking changes since the
|
||||||
|
last release of the module.
|
||||||
|
|
||||||
|
1. The `Credentials` type has been moved to the root of the module as it is
|
||||||
|
becoming the core abstraction for the whole module.
|
||||||
|
2. Because of the above mentioned change many functions that previously
|
||||||
|
returned a `TokenProvider` now return `Credentials`. Similarly, these
|
||||||
|
functions have been renamed to be more specific.
|
||||||
|
3. Most places that used to take an optional `TokenProvider` now accept
|
||||||
|
`Credentials`. You can make a `Credentials` from a `TokenProvider` using the
|
||||||
|
constructor found in the `auth` package.
|
||||||
|
4. The `detect` package has been renamed to `credentials`. With this change some
|
||||||
|
function signatures were also updated for better readability.
|
||||||
|
5. Derivative auth flows like `impersonate` and `downscope` have been moved to
|
||||||
|
be under the new `credentials` package.
|
||||||
|
|
||||||
|
Although these changes are disruptive we think that they are for the best of the
|
||||||
|
long-term health of the module. We do not expect any more large breaking changes
|
||||||
|
like these in future revisions, even before 1.0.0. This version will be the
|
||||||
|
first version of the auth library that our client libraries start to use and
|
||||||
|
depend on.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth/credentials/externalaccount:** Add default TokenURL ([#9700](https://github.com/googleapis/google-cloud-go/issues/9700)) ([81830e6](https://github.com/googleapis/google-cloud-go/commit/81830e6848ceefd055aa4d08f933d1154455a0f6))
|
||||||
|
* **auth:** Add downscope.Options.UniverseDomain ([#9634](https://github.com/googleapis/google-cloud-go/issues/9634)) ([52cf7d7](https://github.com/googleapis/google-cloud-go/commit/52cf7d780853594291c4e34302d618299d1f5a1d))
|
||||||
|
* **auth:** Add universe domain to grpctransport and httptransport ([#9663](https://github.com/googleapis/google-cloud-go/issues/9663)) ([67d353b](https://github.com/googleapis/google-cloud-go/commit/67d353beefe3b607c08c891876fbd95ab89e5fe3)), refs [#9670](https://github.com/googleapis/google-cloud-go/issues/9670)
|
||||||
|
* **auth:** Add UniverseDomain to DetectOptions ([#9536](https://github.com/googleapis/google-cloud-go/issues/9536)) ([3618d3f](https://github.com/googleapis/google-cloud-go/commit/3618d3f7061615c0e189f376c75abc201203b501))
|
||||||
|
* **auth:** Make package externalaccount public ([#9633](https://github.com/googleapis/google-cloud-go/issues/9633)) ([a0978d8](https://github.com/googleapis/google-cloud-go/commit/a0978d8e96968399940ebd7d092539772bf9caac))
|
||||||
|
* **auth:** Move credentials to base auth package ([#9590](https://github.com/googleapis/google-cloud-go/issues/9590)) ([1a04baf](https://github.com/googleapis/google-cloud-go/commit/1a04bafa83c27342b9308d785645e1e5423ea10d))
|
||||||
|
* **auth:** Refactor public sigs to use Credentials ([#9603](https://github.com/googleapis/google-cloud-go/issues/9603)) ([69cb240](https://github.com/googleapis/google-cloud-go/commit/69cb240c530b1f7173a9af2555c19e9a1beb56c5))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Update protobuf dep to v1.33.0 ([30b038d](https://github.com/googleapis/google-cloud-go/commit/30b038d8cac0b8cd5dd4761c87f3f298760dd33a))
|
||||||
|
* **auth:** Fix uint32 conversion ([9221c7f](https://github.com/googleapis/google-cloud-go/commit/9221c7fa12cef9d5fb7ddc92f41f1d6204971c7b))
|
||||||
|
* **auth:** Port sts expires fix ([#9618](https://github.com/googleapis/google-cloud-go/issues/9618)) ([7bec97b](https://github.com/googleapis/google-cloud-go/commit/7bec97b2f51ed3ac4f9b88bf100d301da3f5d1bd))
|
||||||
|
* **auth:** Read universe_domain from all credentials files ([#9632](https://github.com/googleapis/google-cloud-go/issues/9632)) ([16efbb5](https://github.com/googleapis/google-cloud-go/commit/16efbb52e39ea4a319e5ee1e95c0e0305b6d9824))
|
||||||
|
* **auth:** Remove content-type header from idms get requests ([#9508](https://github.com/googleapis/google-cloud-go/issues/9508)) ([8589f41](https://github.com/googleapis/google-cloud-go/commit/8589f41599d265d7c3d46a3d86c9fab2329cbdd9))
|
||||||
|
* **auth:** Update protobuf dep to v1.33.0 ([30b038d](https://github.com/googleapis/google-cloud-go/commit/30b038d8cac0b8cd5dd4761c87f3f298760dd33a))
|
||||||
|
|
||||||
|
## [0.1.1](https://github.com/googleapis/google-cloud-go/compare/auth/v0.1.0...auth/v0.1.1) (2024-03-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/impersonate:** Properly send default detect params ([#9529](https://github.com/googleapis/google-cloud-go/issues/9529)) ([5b6b8be](https://github.com/googleapis/google-cloud-go/commit/5b6b8bef577f82707e51f5cc5d258d5bdf90218f)), refs [#9136](https://github.com/googleapis/google-cloud-go/issues/9136)
|
||||||
|
* **auth:** Update grpc-go to v1.56.3 ([343cea8](https://github.com/googleapis/google-cloud-go/commit/343cea8c43b1e31ae21ad50ad31d3b0b60143f8c))
|
||||||
|
* **auth:** Update grpc-go to v1.59.0 ([81a97b0](https://github.com/googleapis/google-cloud-go/commit/81a97b06cb28b25432e4ece595c55a9857e960b7))
|
||||||
|
|
||||||
|
## 0.1.0 (2023-10-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth:** Add base auth package ([#8465](https://github.com/googleapis/google-cloud-go/issues/8465)) ([6a45f26](https://github.com/googleapis/google-cloud-go/commit/6a45f26b809b64edae21f312c18d4205f96b180e))
|
||||||
|
* **auth:** Add cert support to httptransport ([#8569](https://github.com/googleapis/google-cloud-go/issues/8569)) ([37e3435](https://github.com/googleapis/google-cloud-go/commit/37e3435f8e98595eafab481bdfcb31a4c56fa993))
|
||||||
|
* **auth:** Add Credentials.UniverseDomain() ([#8654](https://github.com/googleapis/google-cloud-go/issues/8654)) ([af0aa1e](https://github.com/googleapis/google-cloud-go/commit/af0aa1ed8015bc8fe0dd87a7549ae029107cbdb8))
|
||||||
|
* **auth:** Add detect package ([#8491](https://github.com/googleapis/google-cloud-go/issues/8491)) ([d977419](https://github.com/googleapis/google-cloud-go/commit/d977419a3269f6acc193df77a2136a6eb4b4add7))
|
||||||
|
* **auth:** Add downscope package ([#8532](https://github.com/googleapis/google-cloud-go/issues/8532)) ([dda9bff](https://github.com/googleapis/google-cloud-go/commit/dda9bff8ec70e6d104901b4105d13dcaa4e2404c))
|
||||||
|
* **auth:** Add grpctransport package ([#8625](https://github.com/googleapis/google-cloud-go/issues/8625)) ([69a8347](https://github.com/googleapis/google-cloud-go/commit/69a83470bdcc7ed10c6c36d1abc3b7cfdb8a0ee5))
|
||||||
|
* **auth:** Add httptransport package ([#8567](https://github.com/googleapis/google-cloud-go/issues/8567)) ([6898597](https://github.com/googleapis/google-cloud-go/commit/6898597d2ea95d630fcd00fd15c58c75ea843bff))
|
||||||
|
* **auth:** Add idtoken package ([#8580](https://github.com/googleapis/google-cloud-go/issues/8580)) ([a79e693](https://github.com/googleapis/google-cloud-go/commit/a79e693e97e4e3e1c6742099af3dbc58866d88fe))
|
||||||
|
* **auth:** Add impersonate package ([#8578](https://github.com/googleapis/google-cloud-go/issues/8578)) ([e29ba0c](https://github.com/googleapis/google-cloud-go/commit/e29ba0cb7bd3888ab9e808087027dc5a32474c04))
|
||||||
|
* **auth:** Add support for external accounts in detect ([#8508](https://github.com/googleapis/google-cloud-go/issues/8508)) ([62210d5](https://github.com/googleapis/google-cloud-go/commit/62210d5d3e56e8e9f35db8e6ac0defec19582507))
|
||||||
|
* **auth:** Port external account changes ([#8697](https://github.com/googleapis/google-cloud-go/issues/8697)) ([5823db5](https://github.com/googleapis/google-cloud-go/commit/5823db5d633069999b58b9131a7f9cd77e82c899))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Update golang.org/x/net to v0.17.0 ([174da47](https://github.com/googleapis/google-cloud-go/commit/174da47254fefb12921bbfc65b7829a453af6f5d))
|
||||||
|
* **auth:** Update golang.org/x/net to v0.17.0 ([174da47](https://github.com/googleapis/google-cloud-go/commit/174da47254fefb12921bbfc65b7829a453af6f5d))
|
||||||
202
vendor/cloud.google.com/go/auth/LICENSE
generated
vendored
Normal file
202
vendor/cloud.google.com/go/auth/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
40
vendor/cloud.google.com/go/auth/README.md
generated
vendored
Normal file
40
vendor/cloud.google.com/go/auth/README.md
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Google Auth Library for Go
|
||||||
|
|
||||||
|
[](https://pkg.go.dev/cloud.google.com/go/auth)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
go get cloud.google.com/go/auth@latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The most common way this library is used is transitively, by default, from any
|
||||||
|
of our Go client libraries.
|
||||||
|
|
||||||
|
### Notable use-cases
|
||||||
|
|
||||||
|
- To create a credential directly please see examples in the
|
||||||
|
[credentials](https://pkg.go.dev/cloud.google.com/go/auth/credentials)
|
||||||
|
package.
|
||||||
|
- To create a authenticated HTTP client please see examples in the
|
||||||
|
[httptransport](https://pkg.go.dev/cloud.google.com/go/auth/httptransport)
|
||||||
|
package.
|
||||||
|
- To create a authenticated gRPC connection please see examples in the
|
||||||
|
[grpctransport](https://pkg.go.dev/cloud.google.com/go/auth/grpctransport)
|
||||||
|
package.
|
||||||
|
- To create an ID token please see examples in the
|
||||||
|
[idtoken](https://pkg.go.dev/cloud.google.com/go/auth/credentials/idtoken)
|
||||||
|
package.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome. Please, see the
|
||||||
|
[CONTRIBUTING](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/main/CONTRIBUTING.md)
|
||||||
|
document for details.
|
||||||
|
|
||||||
|
Please note that this project is released with a Contributor Code of Conduct.
|
||||||
|
By participating in this project you agree to abide by its terms.
|
||||||
|
See [Contributor Code of Conduct](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/main/CONTRIBUTING.md#contributor-code-of-conduct)
|
||||||
|
for more information.
|
||||||
618
vendor/cloud.google.com/go/auth/auth.go
generated
vendored
Normal file
618
vendor/cloud.google.com/go/auth/auth.go
generated
vendored
Normal file
@@ -0,0 +1,618 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package auth provides utilities for managing Google Cloud credentials,
|
||||||
|
// including functionality for creating, caching, and refreshing OAuth2 tokens.
|
||||||
|
// It offers customizable options for different OAuth2 flows, such as 2-legged
|
||||||
|
// (2LO) and 3-legged (3LO) OAuth, along with support for PKCE and automatic
|
||||||
|
// token management.
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"cloud.google.com/go/auth/internal/jwt"
|
||||||
|
"github.com/googleapis/gax-go/v2/internallog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Parameter keys for AuthCodeURL method to support PKCE.
|
||||||
|
codeChallengeKey = "code_challenge"
|
||||||
|
codeChallengeMethodKey = "code_challenge_method"
|
||||||
|
|
||||||
|
// Parameter key for Exchange method to support PKCE.
|
||||||
|
codeVerifierKey = "code_verifier"
|
||||||
|
|
||||||
|
// 3 minutes and 45 seconds before expiration. The shortest MDS cache is 4 minutes,
|
||||||
|
// so we give it 15 seconds to refresh it's cache before attempting to refresh a token.
|
||||||
|
defaultExpiryDelta = 225 * time.Second
|
||||||
|
|
||||||
|
universeDomainDefault = "googleapis.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
// tokenState represents different states for a [Token].
|
||||||
|
type tokenState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// fresh indicates that the [Token] is valid. It is not expired or close to
|
||||||
|
// expired, or the token has no expiry.
|
||||||
|
fresh tokenState = iota
|
||||||
|
// stale indicates that the [Token] is close to expired, and should be
|
||||||
|
// refreshed. The token can be used normally.
|
||||||
|
stale
|
||||||
|
// invalid indicates that the [Token] is expired or invalid. The token
|
||||||
|
// cannot be used for a normal operation.
|
||||||
|
invalid
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
||||||
|
defaultHeader = &jwt.Header{Algorithm: jwt.HeaderAlgRSA256, Type: jwt.HeaderType}
|
||||||
|
|
||||||
|
// for testing
|
||||||
|
timeNow = time.Now
|
||||||
|
)
|
||||||
|
|
||||||
|
// TokenProvider specifies an interface for anything that can return a token.
|
||||||
|
type TokenProvider interface {
|
||||||
|
// Token returns a Token or an error.
|
||||||
|
// The Token returned must be safe to use
|
||||||
|
// concurrently.
|
||||||
|
// The returned Token must not be modified.
|
||||||
|
// The context provided must be sent along to any requests that are made in
|
||||||
|
// the implementing code.
|
||||||
|
Token(context.Context) (*Token, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token holds the credential token used to authorized requests. All fields are
|
||||||
|
// considered read-only.
|
||||||
|
type Token struct {
|
||||||
|
// Value is the token used to authorize requests. It is usually an access
|
||||||
|
// token but may be other types of tokens such as ID tokens in some flows.
|
||||||
|
Value string
|
||||||
|
// Type is the type of token Value is. If uninitialized, it should be
|
||||||
|
// assumed to be a "Bearer" token.
|
||||||
|
Type string
|
||||||
|
// Expiry is the time the token is set to expire.
|
||||||
|
Expiry time.Time
|
||||||
|
// Metadata may include, but is not limited to, the body of the token
|
||||||
|
// response returned by the server.
|
||||||
|
Metadata map[string]interface{} // TODO(codyoss): maybe make a method to flatten metadata to avoid []string for url.Values
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsValid reports that a [Token] is non-nil, has a [Token.Value], and has not
|
||||||
|
// expired. A token is considered expired if [Token.Expiry] has passed or will
|
||||||
|
// pass in the next 225 seconds.
|
||||||
|
func (t *Token) IsValid() bool {
|
||||||
|
return t.isValidWithEarlyExpiry(defaultExpiryDelta)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetadataString is a convenience method for accessing string values in the
|
||||||
|
// token's metadata. Returns an empty string if the metadata is nil or the value
|
||||||
|
// for the given key cannot be cast to a string.
|
||||||
|
func (t *Token) MetadataString(k string) string {
|
||||||
|
if t.Metadata == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
s, ok := t.Metadata[k].(string)
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) isValidWithEarlyExpiry(earlyExpiry time.Duration) bool {
|
||||||
|
if t.isEmpty() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if t.Expiry.IsZero() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !t.Expiry.Round(0).Add(-earlyExpiry).Before(timeNow())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) isEmpty() bool {
|
||||||
|
return t == nil || t.Value == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Credentials holds Google credentials, including
|
||||||
|
// [Application Default Credentials].
|
||||||
|
//
|
||||||
|
// [Application Default Credentials]: https://developers.google.com/accounts/docs/application-default-credentials
|
||||||
|
type Credentials struct {
|
||||||
|
json []byte
|
||||||
|
projectID CredentialsPropertyProvider
|
||||||
|
quotaProjectID CredentialsPropertyProvider
|
||||||
|
// universeDomain is the default service domain for a given Cloud universe.
|
||||||
|
universeDomain CredentialsPropertyProvider
|
||||||
|
|
||||||
|
TokenProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON returns the bytes associated with the the file used to source
|
||||||
|
// credentials if one was used.
|
||||||
|
func (c *Credentials) JSON() []byte {
|
||||||
|
return c.json
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectID returns the associated project ID from the underlying file or
|
||||||
|
// environment.
|
||||||
|
func (c *Credentials) ProjectID(ctx context.Context) (string, error) {
|
||||||
|
if c.projectID == nil {
|
||||||
|
return internal.GetProjectID(c.json, ""), nil
|
||||||
|
}
|
||||||
|
v, err := c.projectID.GetProperty(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return internal.GetProjectID(c.json, v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// QuotaProjectID returns the associated quota project ID from the underlying
|
||||||
|
// file or environment.
|
||||||
|
func (c *Credentials) QuotaProjectID(ctx context.Context) (string, error) {
|
||||||
|
if c.quotaProjectID == nil {
|
||||||
|
return internal.GetQuotaProject(c.json, ""), nil
|
||||||
|
}
|
||||||
|
v, err := c.quotaProjectID.GetProperty(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return internal.GetQuotaProject(c.json, v), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UniverseDomain returns the default service domain for a given Cloud universe.
|
||||||
|
// The default value is "googleapis.com".
|
||||||
|
func (c *Credentials) UniverseDomain(ctx context.Context) (string, error) {
|
||||||
|
if c.universeDomain == nil {
|
||||||
|
return universeDomainDefault, nil
|
||||||
|
}
|
||||||
|
v, err := c.universeDomain.GetProperty(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if v == "" {
|
||||||
|
return universeDomainDefault, nil
|
||||||
|
}
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CredentialsPropertyProvider provides an implementation to fetch a property
|
||||||
|
// value for [Credentials].
|
||||||
|
type CredentialsPropertyProvider interface {
|
||||||
|
GetProperty(context.Context) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CredentialsPropertyFunc is a type adapter to allow the use of ordinary
|
||||||
|
// functions as a [CredentialsPropertyProvider].
|
||||||
|
type CredentialsPropertyFunc func(context.Context) (string, error)
|
||||||
|
|
||||||
|
// GetProperty loads the properly value provided the given context.
|
||||||
|
func (p CredentialsPropertyFunc) GetProperty(ctx context.Context) (string, error) {
|
||||||
|
return p(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CredentialsOptions are used to configure [Credentials].
|
||||||
|
type CredentialsOptions struct {
|
||||||
|
// TokenProvider is a means of sourcing a token for the credentials. Required.
|
||||||
|
TokenProvider TokenProvider
|
||||||
|
// JSON is the raw contents of the credentials file if sourced from a file.
|
||||||
|
JSON []byte
|
||||||
|
// ProjectIDProvider resolves the project ID associated with the
|
||||||
|
// credentials.
|
||||||
|
ProjectIDProvider CredentialsPropertyProvider
|
||||||
|
// QuotaProjectIDProvider resolves the quota project ID associated with the
|
||||||
|
// credentials.
|
||||||
|
QuotaProjectIDProvider CredentialsPropertyProvider
|
||||||
|
// UniverseDomainProvider resolves the universe domain with the credentials.
|
||||||
|
UniverseDomainProvider CredentialsPropertyProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentials returns new [Credentials] from the provided options.
|
||||||
|
func NewCredentials(opts *CredentialsOptions) *Credentials {
|
||||||
|
creds := &Credentials{
|
||||||
|
TokenProvider: opts.TokenProvider,
|
||||||
|
json: opts.JSON,
|
||||||
|
projectID: opts.ProjectIDProvider,
|
||||||
|
quotaProjectID: opts.QuotaProjectIDProvider,
|
||||||
|
universeDomain: opts.UniverseDomainProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
return creds
|
||||||
|
}
|
||||||
|
|
||||||
|
// CachedTokenProviderOptions provides options for configuring a cached
|
||||||
|
// [TokenProvider].
|
||||||
|
type CachedTokenProviderOptions struct {
|
||||||
|
// DisableAutoRefresh makes the TokenProvider always return the same token,
|
||||||
|
// even if it is expired. The default is false. Optional.
|
||||||
|
DisableAutoRefresh bool
|
||||||
|
// ExpireEarly configures the amount of time before a token expires, that it
|
||||||
|
// should be refreshed. If unset, the default value is 3 minutes and 45
|
||||||
|
// seconds. Optional.
|
||||||
|
ExpireEarly time.Duration
|
||||||
|
// DisableAsyncRefresh configures a synchronous workflow that refreshes
|
||||||
|
// tokens in a blocking manner. The default is false. Optional.
|
||||||
|
DisableAsyncRefresh bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctpo *CachedTokenProviderOptions) autoRefresh() bool {
|
||||||
|
if ctpo == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !ctpo.DisableAutoRefresh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctpo *CachedTokenProviderOptions) expireEarly() time.Duration {
|
||||||
|
if ctpo == nil || ctpo.ExpireEarly == 0 {
|
||||||
|
return defaultExpiryDelta
|
||||||
|
}
|
||||||
|
return ctpo.ExpireEarly
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctpo *CachedTokenProviderOptions) blockingRefresh() bool {
|
||||||
|
if ctpo == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ctpo.DisableAsyncRefresh
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCachedTokenProvider wraps a [TokenProvider] to cache the tokens returned
|
||||||
|
// by the underlying provider. By default it will refresh tokens asynchronously
|
||||||
|
// a few minutes before they expire.
|
||||||
|
func NewCachedTokenProvider(tp TokenProvider, opts *CachedTokenProviderOptions) TokenProvider {
|
||||||
|
if ctp, ok := tp.(*cachedTokenProvider); ok {
|
||||||
|
return ctp
|
||||||
|
}
|
||||||
|
return &cachedTokenProvider{
|
||||||
|
tp: tp,
|
||||||
|
autoRefresh: opts.autoRefresh(),
|
||||||
|
expireEarly: opts.expireEarly(),
|
||||||
|
blockingRefresh: opts.blockingRefresh(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type cachedTokenProvider struct {
|
||||||
|
tp TokenProvider
|
||||||
|
autoRefresh bool
|
||||||
|
expireEarly time.Duration
|
||||||
|
blockingRefresh bool
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
cachedToken *Token
|
||||||
|
// isRefreshRunning ensures that the non-blocking refresh will only be
|
||||||
|
// attempted once, even if multiple callers enter the Token method.
|
||||||
|
isRefreshRunning bool
|
||||||
|
// isRefreshErr ensures that the non-blocking refresh will only be attempted
|
||||||
|
// once per refresh window if an error is encountered.
|
||||||
|
isRefreshErr bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cachedTokenProvider) Token(ctx context.Context) (*Token, error) {
|
||||||
|
if c.blockingRefresh {
|
||||||
|
return c.tokenBlocking(ctx)
|
||||||
|
}
|
||||||
|
return c.tokenNonBlocking(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cachedTokenProvider) tokenNonBlocking(ctx context.Context) (*Token, error) {
|
||||||
|
switch c.tokenState() {
|
||||||
|
case fresh:
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
return c.cachedToken, nil
|
||||||
|
case stale:
|
||||||
|
// Call tokenAsync with a new Context because the user-provided context
|
||||||
|
// may have a short timeout incompatible with async token refresh.
|
||||||
|
c.tokenAsync(context.Background())
|
||||||
|
// Return the stale token immediately to not block customer requests to Cloud services.
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
return c.cachedToken, nil
|
||||||
|
default: // invalid
|
||||||
|
return c.tokenBlocking(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tokenState reports the token's validity.
|
||||||
|
func (c *cachedTokenProvider) tokenState() tokenState {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
t := c.cachedToken
|
||||||
|
now := timeNow()
|
||||||
|
if t == nil || t.Value == "" {
|
||||||
|
return invalid
|
||||||
|
} else if t.Expiry.IsZero() {
|
||||||
|
return fresh
|
||||||
|
} else if now.After(t.Expiry.Round(0)) {
|
||||||
|
return invalid
|
||||||
|
} else if now.After(t.Expiry.Round(0).Add(-c.expireEarly)) {
|
||||||
|
return stale
|
||||||
|
}
|
||||||
|
return fresh
|
||||||
|
}
|
||||||
|
|
||||||
|
// tokenAsync uses a bool to ensure that only one non-blocking token refresh
|
||||||
|
// happens at a time, even if multiple callers have entered this function
|
||||||
|
// concurrently. This avoids creating an arbitrary number of concurrent
|
||||||
|
// goroutines. Retries should be attempted and managed within the Token method.
|
||||||
|
// If the refresh attempt fails, no further attempts are made until the refresh
|
||||||
|
// window expires and the token enters the invalid state, at which point the
|
||||||
|
// blocking call to Token should likely return the same error on the main goroutine.
|
||||||
|
func (c *cachedTokenProvider) tokenAsync(ctx context.Context) {
|
||||||
|
fn := func() {
|
||||||
|
t, err := c.tp.Token(ctx)
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
c.isRefreshRunning = false
|
||||||
|
if err != nil {
|
||||||
|
// Discard errors from the non-blocking refresh, but prevent further
|
||||||
|
// attempts.
|
||||||
|
c.isRefreshErr = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.cachedToken = t
|
||||||
|
}
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
if !c.isRefreshRunning && !c.isRefreshErr {
|
||||||
|
c.isRefreshRunning = true
|
||||||
|
go fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cachedTokenProvider) tokenBlocking(ctx context.Context) (*Token, error) {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
c.isRefreshErr = false
|
||||||
|
if c.cachedToken.IsValid() || (!c.autoRefresh && !c.cachedToken.isEmpty()) {
|
||||||
|
return c.cachedToken, nil
|
||||||
|
}
|
||||||
|
t, err := c.tp.Token(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.cachedToken = t
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error is a error associated with retrieving a [Token]. It can hold useful
|
||||||
|
// additional details for debugging.
|
||||||
|
type Error struct {
|
||||||
|
// Response is the HTTP response associated with error. The body will always
|
||||||
|
// be already closed and consumed.
|
||||||
|
Response *http.Response
|
||||||
|
// Body is the HTTP response body.
|
||||||
|
Body []byte
|
||||||
|
// Err is the underlying wrapped error.
|
||||||
|
Err error
|
||||||
|
|
||||||
|
// code returned in the token response
|
||||||
|
code string
|
||||||
|
// description returned in the token response
|
||||||
|
description string
|
||||||
|
// uri returned in the token response
|
||||||
|
uri string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
if e.code != "" {
|
||||||
|
s := fmt.Sprintf("auth: %q", e.code)
|
||||||
|
if e.description != "" {
|
||||||
|
s += fmt.Sprintf(" %q", e.description)
|
||||||
|
}
|
||||||
|
if e.uri != "" {
|
||||||
|
s += fmt.Sprintf(" %q", e.uri)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("auth: cannot fetch token: %v\nResponse: %s", e.Response.StatusCode, e.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary returns true if the error is considered temporary and may be able
|
||||||
|
// to be retried.
|
||||||
|
func (e *Error) Temporary() bool {
|
||||||
|
if e.Response == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
sc := e.Response.StatusCode
|
||||||
|
return sc == http.StatusInternalServerError || sc == http.StatusServiceUnavailable || sc == http.StatusRequestTimeout || sc == http.StatusTooManyRequests
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Unwrap() error {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Style describes how the token endpoint wants to receive the ClientID and
|
||||||
|
// ClientSecret.
|
||||||
|
type Style int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// StyleUnknown means the value has not been initiated. Sending this in
|
||||||
|
// a request will cause the token exchange to fail.
|
||||||
|
StyleUnknown Style = iota
|
||||||
|
// StyleInParams sends client info in the body of a POST request.
|
||||||
|
StyleInParams
|
||||||
|
// StyleInHeader sends client info using Basic Authorization header.
|
||||||
|
StyleInHeader
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options2LO is the configuration settings for doing a 2-legged JWT OAuth2 flow.
|
||||||
|
type Options2LO struct {
|
||||||
|
// Email is the OAuth2 client ID. This value is set as the "iss" in the
|
||||||
|
// JWT.
|
||||||
|
Email string
|
||||||
|
// PrivateKey contains the contents of an RSA private key or the
|
||||||
|
// contents of a PEM file that contains a private key. It is used to sign
|
||||||
|
// the JWT created.
|
||||||
|
PrivateKey []byte
|
||||||
|
// TokenURL is th URL the JWT is sent to. Required.
|
||||||
|
TokenURL string
|
||||||
|
// PrivateKeyID is the ID of the key used to sign the JWT. It is used as the
|
||||||
|
// "kid" in the JWT header. Optional.
|
||||||
|
PrivateKeyID string
|
||||||
|
// Subject is the used for to impersonate a user. It is used as the "sub" in
|
||||||
|
// the JWT.m Optional.
|
||||||
|
Subject string
|
||||||
|
// Scopes specifies requested permissions for the token. Optional.
|
||||||
|
Scopes []string
|
||||||
|
// Expires specifies the lifetime of the token. Optional.
|
||||||
|
Expires time.Duration
|
||||||
|
// Audience specifies the "aud" in the JWT. Optional.
|
||||||
|
Audience string
|
||||||
|
// PrivateClaims allows specifying any custom claims for the JWT. Optional.
|
||||||
|
PrivateClaims map[string]interface{}
|
||||||
|
// UniverseDomain is the default service domain for a given Cloud universe.
|
||||||
|
UniverseDomain string
|
||||||
|
|
||||||
|
// Client is the client to be used to make the underlying token requests.
|
||||||
|
// Optional.
|
||||||
|
Client *http.Client
|
||||||
|
// UseIDToken requests that the token returned be an ID token if one is
|
||||||
|
// returned from the server. Optional.
|
||||||
|
UseIDToken bool
|
||||||
|
// Logger is used for debug logging. If provided, logging will be enabled
|
||||||
|
// at the loggers configured level. By default logging is disabled unless
|
||||||
|
// enabled by setting GOOGLE_SDK_GO_LOGGING_LEVEL in which case a default
|
||||||
|
// logger will be used. Optional.
|
||||||
|
Logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options2LO) client() *http.Client {
|
||||||
|
if o.Client != nil {
|
||||||
|
return o.Client
|
||||||
|
}
|
||||||
|
return internal.DefaultClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options2LO) validate() error {
|
||||||
|
if o == nil {
|
||||||
|
return errors.New("auth: options must be provided")
|
||||||
|
}
|
||||||
|
if o.Email == "" {
|
||||||
|
return errors.New("auth: email must be provided")
|
||||||
|
}
|
||||||
|
if len(o.PrivateKey) == 0 {
|
||||||
|
return errors.New("auth: private key must be provided")
|
||||||
|
}
|
||||||
|
if o.TokenURL == "" {
|
||||||
|
return errors.New("auth: token URL must be provided")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New2LOTokenProvider returns a [TokenProvider] from the provided options.
|
||||||
|
func New2LOTokenProvider(opts *Options2LO) (TokenProvider, error) {
|
||||||
|
if err := opts.validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tokenProvider2LO{opts: opts, Client: opts.client(), logger: internallog.New(opts.Logger)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenProvider2LO struct {
|
||||||
|
opts *Options2LO
|
||||||
|
Client *http.Client
|
||||||
|
logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tp tokenProvider2LO) Token(ctx context.Context) (*Token, error) {
|
||||||
|
pk, err := internal.ParseKey(tp.opts.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
claimSet := &jwt.Claims{
|
||||||
|
Iss: tp.opts.Email,
|
||||||
|
Scope: strings.Join(tp.opts.Scopes, " "),
|
||||||
|
Aud: tp.opts.TokenURL,
|
||||||
|
AdditionalClaims: tp.opts.PrivateClaims,
|
||||||
|
Sub: tp.opts.Subject,
|
||||||
|
}
|
||||||
|
if t := tp.opts.Expires; t > 0 {
|
||||||
|
claimSet.Exp = time.Now().Add(t).Unix()
|
||||||
|
}
|
||||||
|
if aud := tp.opts.Audience; aud != "" {
|
||||||
|
claimSet.Aud = aud
|
||||||
|
}
|
||||||
|
h := *defaultHeader
|
||||||
|
h.KeyID = tp.opts.PrivateKeyID
|
||||||
|
payload, err := jwt.EncodeJWS(&h, claimSet, pk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("grant_type", defaultGrantType)
|
||||||
|
v.Set("assertion", payload)
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", tp.opts.TokenURL, strings.NewReader(v.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
tp.logger.DebugContext(ctx, "2LO token request", "request", internallog.HTTPRequest(req, []byte(v.Encode())))
|
||||||
|
resp, body, err := internal.DoRequest(tp.Client, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("auth: cannot fetch token: %w", err)
|
||||||
|
}
|
||||||
|
tp.logger.DebugContext(ctx, "2LO token response", "response", internallog.HTTPResponse(resp, body))
|
||||||
|
if c := resp.StatusCode; c < http.StatusOK || c >= http.StatusMultipleChoices {
|
||||||
|
return nil, &Error{
|
||||||
|
Response: resp,
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tokenRes is the JSON response body.
|
||||||
|
var tokenRes struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
IDToken string `json:"id_token"`
|
||||||
|
ExpiresIn int64 `json:"expires_in"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &tokenRes); err != nil {
|
||||||
|
return nil, fmt.Errorf("auth: cannot fetch token: %w", err)
|
||||||
|
}
|
||||||
|
token := &Token{
|
||||||
|
Value: tokenRes.AccessToken,
|
||||||
|
Type: tokenRes.TokenType,
|
||||||
|
}
|
||||||
|
token.Metadata = make(map[string]interface{})
|
||||||
|
json.Unmarshal(body, &token.Metadata) // no error checks for optional fields
|
||||||
|
|
||||||
|
if secs := tokenRes.ExpiresIn; secs > 0 {
|
||||||
|
token.Expiry = time.Now().Add(time.Duration(secs) * time.Second)
|
||||||
|
}
|
||||||
|
if v := tokenRes.IDToken; v != "" {
|
||||||
|
// decode returned id token to get expiry
|
||||||
|
claimSet, err := jwt.DecodeJWS(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("auth: error decoding JWT token: %w", err)
|
||||||
|
}
|
||||||
|
token.Expiry = time.Unix(claimSet.Exp, 0)
|
||||||
|
}
|
||||||
|
if tp.opts.UseIDToken {
|
||||||
|
if tokenRes.IDToken == "" {
|
||||||
|
return nil, fmt.Errorf("auth: response doesn't have JWT token")
|
||||||
|
}
|
||||||
|
token.Value = tokenRes.IDToken
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
102
vendor/cloud.google.com/go/auth/credentials/compute.go
generated
vendored
Normal file
102
vendor/cloud.google.com/go/auth/credentials/compute.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"cloud.google.com/go/compute/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
computeTokenMetadata = map[string]interface{}{
|
||||||
|
"auth.google.tokenSource": "compute-metadata",
|
||||||
|
"auth.google.serviceAccount": "default",
|
||||||
|
}
|
||||||
|
computeTokenURI = "instance/service-accounts/default/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// computeTokenProvider creates a [cloud.google.com/go/auth.TokenProvider] that
|
||||||
|
// uses the metadata service to retrieve tokens.
|
||||||
|
func computeTokenProvider(opts *DetectOptions, client *metadata.Client) auth.TokenProvider {
|
||||||
|
return auth.NewCachedTokenProvider(&computeProvider{
|
||||||
|
scopes: opts.Scopes,
|
||||||
|
client: client,
|
||||||
|
tokenBindingType: opts.TokenBindingType,
|
||||||
|
}, &auth.CachedTokenProviderOptions{
|
||||||
|
ExpireEarly: opts.EarlyTokenRefresh,
|
||||||
|
DisableAsyncRefresh: opts.DisableAsyncRefresh,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// computeProvider fetches tokens from the google cloud metadata service.
|
||||||
|
type computeProvider struct {
|
||||||
|
scopes []string
|
||||||
|
client *metadata.Client
|
||||||
|
tokenBindingType TokenBindingType
|
||||||
|
}
|
||||||
|
|
||||||
|
type metadataTokenResp struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
ExpiresInSec int `json:"expires_in"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *computeProvider) Token(ctx context.Context) (*auth.Token, error) {
|
||||||
|
tokenURI, err := url.Parse(computeTokenURI)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hasScopes := len(cs.scopes) > 0
|
||||||
|
if hasScopes || cs.tokenBindingType != NoBinding {
|
||||||
|
v := url.Values{}
|
||||||
|
if hasScopes {
|
||||||
|
v.Set("scopes", strings.Join(cs.scopes, ","))
|
||||||
|
}
|
||||||
|
switch cs.tokenBindingType {
|
||||||
|
case MTLSHardBinding:
|
||||||
|
v.Set("transport", "mtls")
|
||||||
|
v.Set("binding-enforcement", "on")
|
||||||
|
case ALTSHardBinding:
|
||||||
|
v.Set("transport", "alts")
|
||||||
|
}
|
||||||
|
tokenURI.RawQuery = v.Encode()
|
||||||
|
}
|
||||||
|
tokenJSON, err := cs.client.GetWithContext(ctx, tokenURI.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: cannot fetch token: %w", err)
|
||||||
|
}
|
||||||
|
var res metadataTokenResp
|
||||||
|
if err := json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res); err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: invalid token JSON from metadata: %w", err)
|
||||||
|
}
|
||||||
|
if res.ExpiresInSec == 0 || res.AccessToken == "" {
|
||||||
|
return nil, errors.New("credentials: incomplete token received from metadata")
|
||||||
|
}
|
||||||
|
token := &auth.Token{
|
||||||
|
Value: res.AccessToken,
|
||||||
|
Type: res.TokenType,
|
||||||
|
Expiry: time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second),
|
||||||
|
Metadata: computeTokenMetadata,
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
471
vendor/cloud.google.com/go/auth/credentials/detect.go
generated
vendored
Normal file
471
vendor/cloud.google.com/go/auth/credentials/detect.go
generated
vendored
Normal file
@@ -0,0 +1,471 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"cloud.google.com/go/auth/internal/credsfile"
|
||||||
|
"cloud.google.com/go/auth/internal/trustboundary"
|
||||||
|
"cloud.google.com/go/compute/metadata"
|
||||||
|
"github.com/googleapis/gax-go/v2/internallog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// jwtTokenURL is Google's OAuth 2.0 token URL to use with the JWT(2LO) flow.
|
||||||
|
jwtTokenURL = "https://oauth2.googleapis.com/token"
|
||||||
|
|
||||||
|
// Google's OAuth 2.0 default endpoints.
|
||||||
|
googleAuthURL = "https://accounts.google.com/o/oauth2/auth"
|
||||||
|
googleTokenURL = "https://oauth2.googleapis.com/token"
|
||||||
|
|
||||||
|
// GoogleMTLSTokenURL is Google's default OAuth2.0 mTLS endpoint.
|
||||||
|
GoogleMTLSTokenURL = "https://oauth2.mtls.googleapis.com/token"
|
||||||
|
|
||||||
|
// Help on default credentials
|
||||||
|
adcSetupURL = "https://cloud.google.com/docs/authentication/external/set-up-adc"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// for testing
|
||||||
|
allowOnGCECheck = true
|
||||||
|
)
|
||||||
|
|
||||||
|
// CredType specifies the type of JSON credentials being provided
|
||||||
|
// to a loading function such as [NewCredentialsFromFile] or
|
||||||
|
// [NewCredentialsFromJSON].
|
||||||
|
type CredType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ServiceAccount represents a service account file type.
|
||||||
|
ServiceAccount CredType = "service_account"
|
||||||
|
// AuthorizedUser represents a user credentials file type.
|
||||||
|
AuthorizedUser CredType = "authorized_user"
|
||||||
|
// ExternalAccount represents an external account file type.
|
||||||
|
//
|
||||||
|
// IMPORTANT:
|
||||||
|
// This credential type does not validate the credential configuration. A security
|
||||||
|
// risk occurs when a credential configuration configured with malicious urls
|
||||||
|
// is used.
|
||||||
|
// You should validate credential configurations provided by untrusted sources.
|
||||||
|
// See [Security requirements when using credential configurations from an external
|
||||||
|
// source] https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
|
||||||
|
// for more details.
|
||||||
|
ExternalAccount CredType = "external_account"
|
||||||
|
// ImpersonatedServiceAccount represents an impersonated service account file type.
|
||||||
|
//
|
||||||
|
// IMPORTANT:
|
||||||
|
// This credential type does not validate the credential configuration. A security
|
||||||
|
// risk occurs when a credential configuration configured with malicious urls
|
||||||
|
// is used.
|
||||||
|
// You should validate credential configurations provided by untrusted sources.
|
||||||
|
// See [Security requirements when using credential configurations from an external
|
||||||
|
// source] https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
|
||||||
|
// for more details.
|
||||||
|
ImpersonatedServiceAccount CredType = "impersonated_service_account"
|
||||||
|
// GDCHServiceAccount represents a GDCH service account credentials.
|
||||||
|
GDCHServiceAccount CredType = "gdch_service_account"
|
||||||
|
// ExternalAccountAuthorizedUser represents an external account authorized user credentials.
|
||||||
|
ExternalAccountAuthorizedUser CredType = "external_account_authorized_user"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TokenBindingType specifies the type of binding used when requesting a token
|
||||||
|
// whether to request a hard-bound token using mTLS or an instance identity
|
||||||
|
// bound token using ALTS.
|
||||||
|
type TokenBindingType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// NoBinding specifies that requested tokens are not required to have a
|
||||||
|
// binding. This is the default option.
|
||||||
|
NoBinding TokenBindingType = iota
|
||||||
|
// MTLSHardBinding specifies that a hard-bound token should be requested
|
||||||
|
// using an mTLS with S2A channel.
|
||||||
|
MTLSHardBinding
|
||||||
|
// ALTSHardBinding specifies that an instance identity bound token should
|
||||||
|
// be requested using an ALTS channel.
|
||||||
|
ALTSHardBinding
|
||||||
|
)
|
||||||
|
|
||||||
|
// OnGCE reports whether this process is running in Google Cloud.
|
||||||
|
func OnGCE() bool {
|
||||||
|
// TODO(codyoss): once all libs use this auth lib move metadata check here
|
||||||
|
return allowOnGCECheck && metadata.OnGCE()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetectDefault searches for "Application Default Credentials" and returns
|
||||||
|
// a credential based on the [DetectOptions] provided.
|
||||||
|
//
|
||||||
|
// It looks for credentials in the following places, preferring the first
|
||||||
|
// location found:
|
||||||
|
//
|
||||||
|
// - A JSON file whose path is specified by the GOOGLE_APPLICATION_CREDENTIALS
|
||||||
|
// environment variable. For workload identity federation, refer to
|
||||||
|
// https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation
|
||||||
|
// on how to generate the JSON configuration file for on-prem/non-Google
|
||||||
|
// cloud platforms.
|
||||||
|
// - 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.
|
||||||
|
// - On Google Compute Engine, Google App Engine standard second generation
|
||||||
|
// runtimes, and Google App Engine flexible environment, it fetches
|
||||||
|
// credentials from the metadata server.
|
||||||
|
//
|
||||||
|
// Important: If you accept a credential configuration (credential
|
||||||
|
// JSON/File/Stream) from an external source for authentication to Google
|
||||||
|
// Cloud Platform, you must validate it before providing it to any Google
|
||||||
|
// API or library. Providing an unvalidated credential configuration to
|
||||||
|
// Google APIs can compromise the security of your systems and data. For
|
||||||
|
// more information, refer to [Validate credential configurations from
|
||||||
|
// external sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
|
||||||
|
func DetectDefault(opts *DetectOptions) (*auth.Credentials, error) {
|
||||||
|
if err := opts.validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
trustBoundaryEnabled, err := trustboundary.IsEnabled()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(opts.CredentialsJSON) > 0 {
|
||||||
|
return readCredentialsFileJSON(opts.CredentialsJSON, opts)
|
||||||
|
}
|
||||||
|
if opts.CredentialsFile != "" {
|
||||||
|
return readCredentialsFile(opts.CredentialsFile, opts)
|
||||||
|
}
|
||||||
|
if filename := os.Getenv(credsfile.GoogleAppCredsEnvVar); filename != "" {
|
||||||
|
creds, err := readCredentialsFile(filename, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return creds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fileName := credsfile.GetWellKnownFileName()
|
||||||
|
if b, err := os.ReadFile(fileName); err == nil {
|
||||||
|
return readCredentialsFileJSON(b, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
if OnGCE() {
|
||||||
|
metadataClient := metadata.NewWithOptions(&metadata.Options{
|
||||||
|
Logger: opts.logger(),
|
||||||
|
UseDefaultClient: true,
|
||||||
|
})
|
||||||
|
gceUniverseDomainProvider := &internal.ComputeUniverseDomainProvider{
|
||||||
|
MetadataClient: metadataClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
tp := computeTokenProvider(opts, metadataClient)
|
||||||
|
if trustBoundaryEnabled {
|
||||||
|
gceConfigProvider := trustboundary.NewGCEConfigProvider(gceUniverseDomainProvider)
|
||||||
|
var err error
|
||||||
|
tp, err = trustboundary.NewProvider(opts.client(), gceConfigProvider, opts.logger(), tp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: failed to initialize GCE trust boundary provider: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return auth.NewCredentials(&auth.CredentialsOptions{
|
||||||
|
TokenProvider: tp,
|
||||||
|
ProjectIDProvider: auth.CredentialsPropertyFunc(func(ctx context.Context) (string, error) {
|
||||||
|
return metadataClient.ProjectIDWithContext(ctx)
|
||||||
|
}),
|
||||||
|
UniverseDomainProvider: gceUniverseDomainProvider,
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("credentials: could not find default credentials. See %v for more information", adcSetupURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetectOptions provides configuration for [DetectDefault].
|
||||||
|
type DetectOptions struct {
|
||||||
|
// Scopes that credentials tokens should have. Example:
|
||||||
|
// https://www.googleapis.com/auth/cloud-platform. Required if Audience is
|
||||||
|
// not provided.
|
||||||
|
Scopes []string
|
||||||
|
// TokenBindingType specifies the type of binding used when requesting a
|
||||||
|
// token whether to request a hard-bound token using mTLS or an instance
|
||||||
|
// identity bound token using ALTS. Optional.
|
||||||
|
TokenBindingType TokenBindingType
|
||||||
|
// Audience that credentials tokens should have. Only applicable for 2LO
|
||||||
|
// flows with service accounts. If specified, scopes should not be provided.
|
||||||
|
Audience string
|
||||||
|
// Subject is the user email used for [domain wide delegation](https://developers.google.com/identity/protocols/oauth2/service-account#delegatingauthority).
|
||||||
|
// Optional.
|
||||||
|
Subject string
|
||||||
|
// EarlyTokenRefresh configures how early before a token expires that it
|
||||||
|
// should be refreshed. Once the token’s time until expiration has entered
|
||||||
|
// this refresh window the token is considered valid but stale. If unset,
|
||||||
|
// the default value is 3 minutes and 45 seconds. Optional.
|
||||||
|
EarlyTokenRefresh time.Duration
|
||||||
|
// DisableAsyncRefresh configures a synchronous workflow that refreshes
|
||||||
|
// stale tokens while blocking. The default is false. Optional.
|
||||||
|
DisableAsyncRefresh bool
|
||||||
|
// AuthHandlerOptions configures an authorization handler and other options
|
||||||
|
// for 3LO flows. It is required, and only used, for client credential
|
||||||
|
// flows.
|
||||||
|
AuthHandlerOptions *auth.AuthorizationHandlerOptions
|
||||||
|
// TokenURL allows to set the token endpoint for user credential flows. If
|
||||||
|
// unset the default value is: https://oauth2.googleapis.com/token.
|
||||||
|
// Optional.
|
||||||
|
TokenURL string
|
||||||
|
// STSAudience is the audience sent to when retrieving an STS token.
|
||||||
|
// Currently this only used for GDCH auth flow, for which it is required.
|
||||||
|
STSAudience string
|
||||||
|
// CredentialsFile overrides detection logic and sources a credential file
|
||||||
|
// from the provided filepath. If provided, CredentialsJSON must not be.
|
||||||
|
// Optional.
|
||||||
|
//
|
||||||
|
// Deprecated: This field is deprecated because of a potential security risk.
|
||||||
|
// It does not validate the credential configuration. The security risk occurs
|
||||||
|
// when a credential configuration is accepted from a source that is not
|
||||||
|
// under your control and used without validation on your side.
|
||||||
|
//
|
||||||
|
// If you know that you will be loading credential configurations of a
|
||||||
|
// specific type, it is recommended to use a credential-type-specific
|
||||||
|
// NewCredentialsFromFile method. This will ensure that an unexpected
|
||||||
|
// credential type with potential for malicious intent is not loaded
|
||||||
|
// unintentionally. You might still have to do validation for certain
|
||||||
|
// credential types. Please follow the recommendation for that method. For
|
||||||
|
// example, if you want to load only service accounts, you can use
|
||||||
|
//
|
||||||
|
// creds, err := credentials.NewCredentialsFromFile(ctx, credentials.ServiceAccount, filename, opts)
|
||||||
|
//
|
||||||
|
// If you are loading your credential configuration from an untrusted source
|
||||||
|
// and have not mitigated the risks (e.g. by validating the configuration
|
||||||
|
// yourself), make these changes as soon as possible to prevent security
|
||||||
|
// risks to your environment.
|
||||||
|
//
|
||||||
|
// Regardless of the method used, it is always your responsibility to
|
||||||
|
// validate configurations received from external sources.
|
||||||
|
//
|
||||||
|
// For more details see:
|
||||||
|
// https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
|
||||||
|
CredentialsFile string
|
||||||
|
// CredentialsJSON overrides detection logic and uses the JSON bytes as the
|
||||||
|
// source for the credential. If provided, CredentialsFile must not be.
|
||||||
|
// Optional.
|
||||||
|
//
|
||||||
|
// Deprecated: This field is deprecated because of a potential security risk.
|
||||||
|
// It does not validate the credential configuration. The security risk occurs
|
||||||
|
// when a credential configuration is accepted from a source that is not
|
||||||
|
// under your control and used without validation on your side.
|
||||||
|
//
|
||||||
|
// If you know that you will be loading credential configurations of a
|
||||||
|
// specific type, it is recommended to use a credential-type-specific
|
||||||
|
// NewCredentialsFromJSON method. This will ensure that an unexpected
|
||||||
|
// credential type with potential for malicious intent is not loaded
|
||||||
|
// unintentionally. You might still have to do validation for certain
|
||||||
|
// credential types. Please follow the recommendation for that method. For
|
||||||
|
// example, if you want to load only service accounts, you can use
|
||||||
|
//
|
||||||
|
// creds, err := credentials.NewCredentialsFromJSON(ctx, credentials.ServiceAccount, json, opts)
|
||||||
|
//
|
||||||
|
// If you are loading your credential configuration from an untrusted source
|
||||||
|
// and have not mitigated the risks (e.g. by validating the configuration
|
||||||
|
// yourself), make these changes as soon as possible to prevent security
|
||||||
|
// risks to your environment.
|
||||||
|
//
|
||||||
|
// Regardless of the method used, it is always your responsibility to
|
||||||
|
// validate configurations received from external sources.
|
||||||
|
//
|
||||||
|
// For more details see:
|
||||||
|
// https://cloud.google.com/docs/authentication/external/externally-sourced-credentials
|
||||||
|
CredentialsJSON []byte
|
||||||
|
// UseSelfSignedJWT directs service account based credentials to create a
|
||||||
|
// self-signed JWT with the private key found in the file, skipping any
|
||||||
|
// network requests that would normally be made. Optional.
|
||||||
|
UseSelfSignedJWT bool
|
||||||
|
// Client configures the underlying client used to make network requests
|
||||||
|
// when fetching tokens. Optional.
|
||||||
|
Client *http.Client
|
||||||
|
// UniverseDomain is the default service domain for a given Cloud universe.
|
||||||
|
// The default value is "googleapis.com". This option is ignored for
|
||||||
|
// authentication flows that do not support universe domain. Optional.
|
||||||
|
UniverseDomain string
|
||||||
|
// Logger is used for debug logging. If provided, logging will be enabled
|
||||||
|
// at the loggers configured level. By default logging is disabled unless
|
||||||
|
// enabled by setting GOOGLE_SDK_GO_LOGGING_LEVEL in which case a default
|
||||||
|
// logger will be used. Optional.
|
||||||
|
Logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentialsFromFile creates a [cloud.google.com/go/auth.Credentials] from
|
||||||
|
// the provided file. The credType argument specifies the expected credential
|
||||||
|
// type. If the file content does not match the expected type, an error is
|
||||||
|
// returned.
|
||||||
|
//
|
||||||
|
// Important: If you accept a credential configuration (credential
|
||||||
|
// JSON/File/Stream) from an external source for authentication to Google
|
||||||
|
// Cloud Platform, you must validate it before providing it to any Google
|
||||||
|
// API or library. Providing an unvalidated credential configuration to
|
||||||
|
// Google APIs can compromise the security of your systems and data. For
|
||||||
|
// more information, refer to [Validate credential configurations from
|
||||||
|
// external sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
|
||||||
|
func NewCredentialsFromFile(credType CredType, filename string, opts *DetectOptions) (*auth.Credentials, error) {
|
||||||
|
b, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewCredentialsFromJSON(credType, b, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCredentialsFromJSON creates a [cloud.google.com/go/auth.Credentials] from
|
||||||
|
// the provided JSON bytes. The credType argument specifies the expected
|
||||||
|
// credential type. If the JSON does not match the expected type, an error is
|
||||||
|
// returned.
|
||||||
|
//
|
||||||
|
// Important: If you accept a credential configuration (credential
|
||||||
|
// JSON/File/Stream) from an external source for authentication to Google
|
||||||
|
// Cloud Platform, you must validate it before providing it to any Google
|
||||||
|
// API or library. Providing an unvalidated credential configuration to
|
||||||
|
// Google APIs can compromise the security of your systems and data. For
|
||||||
|
// more information, refer to [Validate credential configurations from
|
||||||
|
// external sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
|
||||||
|
func NewCredentialsFromJSON(credType CredType, b []byte, opts *DetectOptions) (*auth.Credentials, error) {
|
||||||
|
if err := checkCredentialType(b, credType); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// We can't use readCredentialsFileJSON because it does auto-detection
|
||||||
|
// for client_credentials.json which we don't support here (no type field).
|
||||||
|
// Instead, we call fileCredentials just as readCredentialsFileJSON does
|
||||||
|
// when it doesn't detect client_credentials.json.
|
||||||
|
return fileCredentials(b, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCredentialType(b []byte, expected CredType) error {
|
||||||
|
|
||||||
|
fileType, err := credsfile.ParseFileType(b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if CredType(fileType) != expected {
|
||||||
|
return fmt.Errorf("credentials: expected type %q, found %q", expected, fileType)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *DetectOptions) validate() error {
|
||||||
|
if o == nil {
|
||||||
|
return errors.New("credentials: options must be provided")
|
||||||
|
}
|
||||||
|
if len(o.Scopes) > 0 && o.Audience != "" {
|
||||||
|
return errors.New("credentials: both scopes and audience were provided")
|
||||||
|
}
|
||||||
|
if len(o.CredentialsJSON) > 0 && o.CredentialsFile != "" {
|
||||||
|
return errors.New("credentials: both credentials file and JSON were provided")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *DetectOptions) tokenURL() string {
|
||||||
|
if o.TokenURL != "" {
|
||||||
|
return o.TokenURL
|
||||||
|
}
|
||||||
|
return googleTokenURL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *DetectOptions) scopes() []string {
|
||||||
|
scopes := make([]string, len(o.Scopes))
|
||||||
|
copy(scopes, o.Scopes)
|
||||||
|
return scopes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *DetectOptions) client() *http.Client {
|
||||||
|
if o.Client != nil {
|
||||||
|
return o.Client
|
||||||
|
}
|
||||||
|
return internal.DefaultClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *DetectOptions) logger() *slog.Logger {
|
||||||
|
return internallog.New(o.Logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCredentialsFile(filename string, opts *DetectOptions) (*auth.Credentials, error) {
|
||||||
|
b, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return readCredentialsFileJSON(b, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readCredentialsFileJSON(b []byte, opts *DetectOptions) (*auth.Credentials, error) {
|
||||||
|
// attempt to parse jsonData as a Google Developers Console client_credentials.json.
|
||||||
|
config := clientCredConfigFromJSON(b, opts)
|
||||||
|
if config != nil {
|
||||||
|
if config.AuthHandlerOpts == nil {
|
||||||
|
return nil, errors.New("credentials: auth handler must be specified for this credential filetype")
|
||||||
|
}
|
||||||
|
tp, err := auth.New3LOTokenProvider(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return auth.NewCredentials(&auth.CredentialsOptions{
|
||||||
|
TokenProvider: tp,
|
||||||
|
JSON: b,
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
return fileCredentials(b, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clientCredConfigFromJSON(b []byte, opts *DetectOptions) *auth.Options3LO {
|
||||||
|
var creds credsfile.ClientCredentialsFile
|
||||||
|
var c *credsfile.Config3LO
|
||||||
|
if err := json.Unmarshal(b, &creds); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case creds.Web != nil:
|
||||||
|
c = creds.Web
|
||||||
|
case creds.Installed != nil:
|
||||||
|
c = creds.Installed
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(c.RedirectURIs) < 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var handleOpts *auth.AuthorizationHandlerOptions
|
||||||
|
if opts.AuthHandlerOptions != nil {
|
||||||
|
handleOpts = &auth.AuthorizationHandlerOptions{
|
||||||
|
Handler: opts.AuthHandlerOptions.Handler,
|
||||||
|
State: opts.AuthHandlerOptions.State,
|
||||||
|
PKCEOpts: opts.AuthHandlerOptions.PKCEOpts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &auth.Options3LO{
|
||||||
|
ClientID: c.ClientID,
|
||||||
|
ClientSecret: c.ClientSecret,
|
||||||
|
RedirectURL: c.RedirectURIs[0],
|
||||||
|
Scopes: opts.scopes(),
|
||||||
|
AuthURL: c.AuthURI,
|
||||||
|
TokenURL: c.TokenURI,
|
||||||
|
Client: opts.client(),
|
||||||
|
Logger: opts.logger(),
|
||||||
|
EarlyTokenExpiry: opts.EarlyTokenRefresh,
|
||||||
|
AuthHandlerOpts: handleOpts,
|
||||||
|
// TODO(codyoss): refactor this out. We need to add in auto-detection
|
||||||
|
// for this use case.
|
||||||
|
AuthStyle: auth.StyleInParams,
|
||||||
|
}
|
||||||
|
}
|
||||||
45
vendor/cloud.google.com/go/auth/credentials/doc.go
generated
vendored
Normal file
45
vendor/cloud.google.com/go/auth/credentials/doc.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package credentials provides support for making OAuth2 authorized and
|
||||||
|
// authenticated HTTP requests to Google APIs. It supports the Web server flow,
|
||||||
|
// client-side credentials, service accounts, Google Compute Engine service
|
||||||
|
// accounts, Google App Engine service accounts and workload identity federation
|
||||||
|
// from non-Google cloud platforms.
|
||||||
|
//
|
||||||
|
// A brief overview of the package follows. For more information, please read
|
||||||
|
// https://developers.google.com/accounts/docs/OAuth2
|
||||||
|
// and
|
||||||
|
// https://developers.google.com/accounts/docs/application-default-credentials.
|
||||||
|
// For more information on using workload identity federation, refer to
|
||||||
|
// https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation.
|
||||||
|
//
|
||||||
|
// # Credentials
|
||||||
|
//
|
||||||
|
// The [cloud.google.com/go/auth.Credentials] type represents Google
|
||||||
|
// credentials, including Application Default Credentials.
|
||||||
|
//
|
||||||
|
// Use [DetectDefault] to obtain Application Default Credentials.
|
||||||
|
//
|
||||||
|
// Application Default Credentials support workload identity federation to
|
||||||
|
// access Google Cloud resources from non-Google Cloud platforms including Amazon
|
||||||
|
// Web Services (AWS), Microsoft Azure or any identity provider that supports
|
||||||
|
// OpenID Connect (OIDC). Workload identity federation is recommended for
|
||||||
|
// non-Google Cloud environments as it avoids the need to download, manage, and
|
||||||
|
// store service account private keys locally.
|
||||||
|
//
|
||||||
|
// # Workforce Identity Federation
|
||||||
|
//
|
||||||
|
// For more information on this feature see [cloud.google.com/go/auth/credentials/externalaccount].
|
||||||
|
package credentials
|
||||||
329
vendor/cloud.google.com/go/auth/credentials/filetypes.go
generated
vendored
Normal file
329
vendor/cloud.google.com/go/auth/credentials/filetypes.go
generated
vendored
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"cloud.google.com/go/auth/credentials/internal/externalaccount"
|
||||||
|
"cloud.google.com/go/auth/credentials/internal/externalaccountuser"
|
||||||
|
"cloud.google.com/go/auth/credentials/internal/gdch"
|
||||||
|
"cloud.google.com/go/auth/credentials/internal/impersonate"
|
||||||
|
internalauth "cloud.google.com/go/auth/internal"
|
||||||
|
"cloud.google.com/go/auth/internal/credsfile"
|
||||||
|
"cloud.google.com/go/auth/internal/trustboundary"
|
||||||
|
)
|
||||||
|
|
||||||
|
const cloudPlatformScope = "https://www.googleapis.com/auth/cloud-platform"
|
||||||
|
|
||||||
|
func fileCredentials(b []byte, opts *DetectOptions) (*auth.Credentials, error) {
|
||||||
|
fileType, err := credsfile.ParseFileType(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if fileType == "" {
|
||||||
|
return nil, errors.New("credentials: unsupported unidentified file type")
|
||||||
|
}
|
||||||
|
|
||||||
|
var projectID, universeDomain string
|
||||||
|
var tp auth.TokenProvider
|
||||||
|
switch CredType(fileType) {
|
||||||
|
case ServiceAccount:
|
||||||
|
f, err := credsfile.ParseServiceAccount(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tp, err = handleServiceAccount(f, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
projectID = f.ProjectID
|
||||||
|
universeDomain = resolveUniverseDomain(opts.UniverseDomain, f.UniverseDomain)
|
||||||
|
case AuthorizedUser:
|
||||||
|
f, err := credsfile.ParseUserCredentials(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tp, err = handleUserCredential(f, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
universeDomain = f.UniverseDomain
|
||||||
|
case ExternalAccount:
|
||||||
|
f, err := credsfile.ParseExternalAccount(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tp, err = handleExternalAccount(f, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
universeDomain = resolveUniverseDomain(opts.UniverseDomain, f.UniverseDomain)
|
||||||
|
case ExternalAccountAuthorizedUser:
|
||||||
|
f, err := credsfile.ParseExternalAccountAuthorizedUser(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tp, err = handleExternalAccountAuthorizedUser(f, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
universeDomain = f.UniverseDomain
|
||||||
|
case ImpersonatedServiceAccount:
|
||||||
|
f, err := credsfile.ParseImpersonatedServiceAccount(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tp, err = handleImpersonatedServiceAccount(f, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
universeDomain = resolveUniverseDomain(opts.UniverseDomain, f.UniverseDomain)
|
||||||
|
case GDCHServiceAccount:
|
||||||
|
f, err := credsfile.ParseGDCHServiceAccount(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tp, err = handleGDCHServiceAccount(f, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
projectID = f.Project
|
||||||
|
universeDomain = f.UniverseDomain
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("credentials: unsupported filetype %q", fileType)
|
||||||
|
}
|
||||||
|
return auth.NewCredentials(&auth.CredentialsOptions{
|
||||||
|
TokenProvider: auth.NewCachedTokenProvider(tp, &auth.CachedTokenProviderOptions{
|
||||||
|
ExpireEarly: opts.EarlyTokenRefresh,
|
||||||
|
}),
|
||||||
|
JSON: b,
|
||||||
|
ProjectIDProvider: internalauth.StaticCredentialsProperty(projectID),
|
||||||
|
// TODO(codyoss): only set quota project here if there was a user override
|
||||||
|
UniverseDomainProvider: internalauth.StaticCredentialsProperty(universeDomain),
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveUniverseDomain returns optsUniverseDomain if non-empty, in order to
|
||||||
|
// support configuring universe-specific credentials in code. Auth flows
|
||||||
|
// unsupported for universe domain should not use this func, but should instead
|
||||||
|
// simply set the file universe domain on the credentials.
|
||||||
|
func resolveUniverseDomain(optsUniverseDomain, fileUniverseDomain string) string {
|
||||||
|
if optsUniverseDomain != "" {
|
||||||
|
return optsUniverseDomain
|
||||||
|
}
|
||||||
|
return fileUniverseDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleServiceAccount(f *credsfile.ServiceAccountFile, opts *DetectOptions) (auth.TokenProvider, error) {
|
||||||
|
ud := resolveUniverseDomain(opts.UniverseDomain, f.UniverseDomain)
|
||||||
|
if opts.UseSelfSignedJWT {
|
||||||
|
return configureSelfSignedJWT(f, opts)
|
||||||
|
} else if ud != "" && ud != internalauth.DefaultUniverseDomain {
|
||||||
|
// For non-GDU universe domains, token exchange is impossible and services
|
||||||
|
// must support self-signed JWTs.
|
||||||
|
opts.UseSelfSignedJWT = true
|
||||||
|
return configureSelfSignedJWT(f, opts)
|
||||||
|
}
|
||||||
|
opts2LO := &auth.Options2LO{
|
||||||
|
Email: f.ClientEmail,
|
||||||
|
PrivateKey: []byte(f.PrivateKey),
|
||||||
|
PrivateKeyID: f.PrivateKeyID,
|
||||||
|
Scopes: opts.scopes(),
|
||||||
|
TokenURL: f.TokenURL,
|
||||||
|
Subject: opts.Subject,
|
||||||
|
Client: opts.client(),
|
||||||
|
Logger: opts.logger(),
|
||||||
|
UniverseDomain: ud,
|
||||||
|
}
|
||||||
|
if opts2LO.TokenURL == "" {
|
||||||
|
opts2LO.TokenURL = jwtTokenURL
|
||||||
|
}
|
||||||
|
|
||||||
|
tp, err := auth.New2LOTokenProvider(opts2LO)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
trustBoundaryEnabled, err := trustboundary.IsEnabled()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !trustBoundaryEnabled {
|
||||||
|
return tp, nil
|
||||||
|
}
|
||||||
|
saConfig := trustboundary.NewServiceAccountConfigProvider(opts2LO.Email, opts2LO.UniverseDomain)
|
||||||
|
return trustboundary.NewProvider(opts.client(), saConfig, opts.logger(), tp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleUserCredential(f *credsfile.UserCredentialsFile, opts *DetectOptions) (auth.TokenProvider, error) {
|
||||||
|
opts3LO := &auth.Options3LO{
|
||||||
|
ClientID: f.ClientID,
|
||||||
|
ClientSecret: f.ClientSecret,
|
||||||
|
Scopes: opts.scopes(),
|
||||||
|
AuthURL: googleAuthURL,
|
||||||
|
TokenURL: opts.tokenURL(),
|
||||||
|
AuthStyle: auth.StyleInParams,
|
||||||
|
EarlyTokenExpiry: opts.EarlyTokenRefresh,
|
||||||
|
RefreshToken: f.RefreshToken,
|
||||||
|
Client: opts.client(),
|
||||||
|
Logger: opts.logger(),
|
||||||
|
}
|
||||||
|
return auth.New3LOTokenProvider(opts3LO)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleExternalAccount(f *credsfile.ExternalAccountFile, opts *DetectOptions) (auth.TokenProvider, error) {
|
||||||
|
externalOpts := &externalaccount.Options{
|
||||||
|
Audience: f.Audience,
|
||||||
|
SubjectTokenType: f.SubjectTokenType,
|
||||||
|
TokenURL: f.TokenURL,
|
||||||
|
TokenInfoURL: f.TokenInfoURL,
|
||||||
|
ServiceAccountImpersonationURL: f.ServiceAccountImpersonationURL,
|
||||||
|
ClientSecret: f.ClientSecret,
|
||||||
|
ClientID: f.ClientID,
|
||||||
|
CredentialSource: f.CredentialSource,
|
||||||
|
QuotaProjectID: f.QuotaProjectID,
|
||||||
|
Scopes: opts.scopes(),
|
||||||
|
WorkforcePoolUserProject: f.WorkforcePoolUserProject,
|
||||||
|
Client: opts.client(),
|
||||||
|
Logger: opts.logger(),
|
||||||
|
IsDefaultClient: opts.Client == nil,
|
||||||
|
}
|
||||||
|
if f.ServiceAccountImpersonation != nil {
|
||||||
|
externalOpts.ServiceAccountImpersonationLifetimeSeconds = f.ServiceAccountImpersonation.TokenLifetimeSeconds
|
||||||
|
}
|
||||||
|
tp, err := externalaccount.NewTokenProvider(externalOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
trustBoundaryEnabled, err := trustboundary.IsEnabled()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !trustBoundaryEnabled {
|
||||||
|
return tp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ud := resolveUniverseDomain(opts.UniverseDomain, f.UniverseDomain)
|
||||||
|
var configProvider trustboundary.ConfigProvider
|
||||||
|
|
||||||
|
if f.ServiceAccountImpersonationURL == "" {
|
||||||
|
// No impersonation, this is a direct external account credential.
|
||||||
|
// The trust boundary is based on the workload/workforce pool.
|
||||||
|
var err error
|
||||||
|
configProvider, err = trustboundary.NewExternalAccountConfigProvider(f.Audience, ud)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Impersonation is used. The trust boundary is based on the target service account.
|
||||||
|
targetSAEmail, err := impersonate.ExtractServiceAccountEmail(f.ServiceAccountImpersonationURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: could not extract target service account email for trust boundary: %w", err)
|
||||||
|
}
|
||||||
|
configProvider = trustboundary.NewServiceAccountConfigProvider(targetSAEmail, ud)
|
||||||
|
}
|
||||||
|
|
||||||
|
return trustboundary.NewProvider(opts.client(), configProvider, opts.logger(), tp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleExternalAccountAuthorizedUser(f *credsfile.ExternalAccountAuthorizedUserFile, opts *DetectOptions) (auth.TokenProvider, error) {
|
||||||
|
externalOpts := &externalaccountuser.Options{
|
||||||
|
Audience: f.Audience,
|
||||||
|
RefreshToken: f.RefreshToken,
|
||||||
|
TokenURL: f.TokenURL,
|
||||||
|
TokenInfoURL: f.TokenInfoURL,
|
||||||
|
ClientID: f.ClientID,
|
||||||
|
ClientSecret: f.ClientSecret,
|
||||||
|
Scopes: opts.scopes(),
|
||||||
|
Client: opts.client(),
|
||||||
|
Logger: opts.logger(),
|
||||||
|
}
|
||||||
|
tp, err := externalaccountuser.NewTokenProvider(externalOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
trustBoundaryEnabled, err := trustboundary.IsEnabled()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !trustBoundaryEnabled {
|
||||||
|
return tp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ud := resolveUniverseDomain(opts.UniverseDomain, f.UniverseDomain)
|
||||||
|
configProvider, err := trustboundary.NewExternalAccountConfigProvider(f.Audience, ud)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return trustboundary.NewProvider(opts.client(), configProvider, opts.logger(), tp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleImpersonatedServiceAccount(f *credsfile.ImpersonatedServiceAccountFile, opts *DetectOptions) (auth.TokenProvider, error) {
|
||||||
|
if f.ServiceAccountImpersonationURL == "" || f.CredSource == nil {
|
||||||
|
return nil, errors.New("missing 'source_credentials' field or 'service_account_impersonation_url' in credentials")
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceOpts := *opts
|
||||||
|
|
||||||
|
// Source credential needs IAM or Cloud Platform scope to call the
|
||||||
|
// iamcredentials endpoint. The scopes provided by the user are for the
|
||||||
|
// impersonated credentials.
|
||||||
|
sourceOpts.Scopes = []string{cloudPlatformScope}
|
||||||
|
sourceTP, err := fileCredentials(f.CredSource, &sourceOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ud := resolveUniverseDomain(opts.UniverseDomain, f.UniverseDomain)
|
||||||
|
scopes := opts.scopes()
|
||||||
|
if len(scopes) == 0 {
|
||||||
|
scopes = f.Scopes
|
||||||
|
}
|
||||||
|
impOpts := &impersonate.Options{
|
||||||
|
URL: f.ServiceAccountImpersonationURL,
|
||||||
|
Scopes: scopes,
|
||||||
|
Tp: sourceTP,
|
||||||
|
Delegates: f.Delegates,
|
||||||
|
Client: opts.client(),
|
||||||
|
Logger: opts.logger(),
|
||||||
|
UniverseDomain: ud,
|
||||||
|
}
|
||||||
|
tp, err := impersonate.NewTokenProvider(impOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
trustBoundaryEnabled, err := trustboundary.IsEnabled()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !trustBoundaryEnabled {
|
||||||
|
return tp, nil
|
||||||
|
}
|
||||||
|
targetSAEmail, err := impersonate.ExtractServiceAccountEmail(f.ServiceAccountImpersonationURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: could not extract target service account email for trust boundary: %w", err)
|
||||||
|
}
|
||||||
|
targetSAConfig := trustboundary.NewServiceAccountConfigProvider(targetSAEmail, ud)
|
||||||
|
return trustboundary.NewProvider(opts.client(), targetSAConfig, opts.logger(), tp)
|
||||||
|
}
|
||||||
|
func handleGDCHServiceAccount(f *credsfile.GDCHServiceAccountFile, opts *DetectOptions) (auth.TokenProvider, error) {
|
||||||
|
return gdch.NewTokenProvider(f, &gdch.Options{
|
||||||
|
STSAudience: opts.STSAudience,
|
||||||
|
Client: opts.client(),
|
||||||
|
Logger: opts.logger(),
|
||||||
|
})
|
||||||
|
}
|
||||||
531
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/aws_provider.go
generated
vendored
Normal file
531
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/aws_provider.go
generated
vendored
Normal file
@@ -0,0 +1,531 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package externalaccount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"github.com/googleapis/gax-go/v2/internallog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// getenv aliases os.Getenv for testing
|
||||||
|
getenv = os.Getenv
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AWS Signature Version 4 signing algorithm identifier.
|
||||||
|
awsAlgorithm = "AWS4-HMAC-SHA256"
|
||||||
|
|
||||||
|
// The termination string for the AWS credential scope value as defined in
|
||||||
|
// https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
|
||||||
|
awsRequestType = "aws4_request"
|
||||||
|
|
||||||
|
// The AWS authorization header name for the security session token if available.
|
||||||
|
awsSecurityTokenHeader = "x-amz-security-token"
|
||||||
|
|
||||||
|
// The name of the header containing the session token for metadata endpoint calls
|
||||||
|
awsIMDSv2SessionTokenHeader = "X-aws-ec2-metadata-token"
|
||||||
|
|
||||||
|
awsIMDSv2SessionTTLHeader = "X-aws-ec2-metadata-token-ttl-seconds"
|
||||||
|
|
||||||
|
awsIMDSv2SessionTTL = "300"
|
||||||
|
|
||||||
|
// The AWS authorization header name for the auto-generated date.
|
||||||
|
awsDateHeader = "x-amz-date"
|
||||||
|
|
||||||
|
defaultRegionalCredentialVerificationURL = "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15"
|
||||||
|
|
||||||
|
// Supported AWS configuration environment variables.
|
||||||
|
awsAccessKeyIDEnvVar = "AWS_ACCESS_KEY_ID"
|
||||||
|
awsDefaultRegionEnvVar = "AWS_DEFAULT_REGION"
|
||||||
|
awsRegionEnvVar = "AWS_REGION"
|
||||||
|
awsSecretAccessKeyEnvVar = "AWS_SECRET_ACCESS_KEY"
|
||||||
|
awsSessionTokenEnvVar = "AWS_SESSION_TOKEN"
|
||||||
|
|
||||||
|
awsTimeFormatLong = "20060102T150405Z"
|
||||||
|
awsTimeFormatShort = "20060102"
|
||||||
|
awsProviderType = "aws"
|
||||||
|
)
|
||||||
|
|
||||||
|
type awsSubjectProvider struct {
|
||||||
|
EnvironmentID string
|
||||||
|
RegionURL string
|
||||||
|
RegionalCredVerificationURL string
|
||||||
|
CredVerificationURL string
|
||||||
|
IMDSv2SessionTokenURL string
|
||||||
|
TargetResource string
|
||||||
|
requestSigner *awsRequestSigner
|
||||||
|
region string
|
||||||
|
securityCredentialsProvider AwsSecurityCredentialsProvider
|
||||||
|
reqOpts *RequestOptions
|
||||||
|
|
||||||
|
Client *http.Client
|
||||||
|
logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *awsSubjectProvider) subjectToken(ctx context.Context) (string, error) {
|
||||||
|
// Set Defaults
|
||||||
|
if sp.RegionalCredVerificationURL == "" {
|
||||||
|
sp.RegionalCredVerificationURL = defaultRegionalCredentialVerificationURL
|
||||||
|
}
|
||||||
|
headers := make(map[string]string)
|
||||||
|
if sp.shouldUseMetadataServer() {
|
||||||
|
awsSessionToken, err := sp.getAWSSessionToken(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if awsSessionToken != "" {
|
||||||
|
headers[awsIMDSv2SessionTokenHeader] = awsSessionToken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
awsSecurityCredentials, err := sp.getSecurityCredentials(ctx, headers)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if sp.region, err = sp.getRegion(ctx, headers); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
sp.requestSigner = &awsRequestSigner{
|
||||||
|
RegionName: sp.region,
|
||||||
|
AwsSecurityCredentials: awsSecurityCredentials,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the signed request to AWS STS GetCallerIdentity API.
|
||||||
|
// Use the required regional endpoint. Otherwise, the request will fail.
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", strings.Replace(sp.RegionalCredVerificationURL, "{region}", sp.region, 1), nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// The full, canonical resource name of the workload identity pool
|
||||||
|
// provider, with or without the HTTPS prefix.
|
||||||
|
// Including this header as part of the signature is recommended to
|
||||||
|
// ensure data integrity.
|
||||||
|
if sp.TargetResource != "" {
|
||||||
|
req.Header.Set("x-goog-cloud-target-resource", sp.TargetResource)
|
||||||
|
}
|
||||||
|
sp.requestSigner.signRequest(req)
|
||||||
|
|
||||||
|
/*
|
||||||
|
The GCP STS endpoint expects the headers to be formatted as:
|
||||||
|
# [
|
||||||
|
# {key: 'x-amz-date', value: '...'},
|
||||||
|
# {key: 'Authorization', value: '...'},
|
||||||
|
# ...
|
||||||
|
# ]
|
||||||
|
# And then serialized as:
|
||||||
|
# quote(json.dumps({
|
||||||
|
# url: '...',
|
||||||
|
# method: 'POST',
|
||||||
|
# headers: [{key: 'x-amz-date', value: '...'}, ...]
|
||||||
|
# }))
|
||||||
|
*/
|
||||||
|
|
||||||
|
awsSignedReq := awsRequest{
|
||||||
|
URL: req.URL.String(),
|
||||||
|
Method: "POST",
|
||||||
|
}
|
||||||
|
for headerKey, headerList := range req.Header {
|
||||||
|
for _, headerValue := range headerList {
|
||||||
|
awsSignedReq.Headers = append(awsSignedReq.Headers, awsRequestHeader{
|
||||||
|
Key: headerKey,
|
||||||
|
Value: headerValue,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Slice(awsSignedReq.Headers, func(i, j int) bool {
|
||||||
|
headerCompare := strings.Compare(awsSignedReq.Headers[i].Key, awsSignedReq.Headers[j].Key)
|
||||||
|
if headerCompare == 0 {
|
||||||
|
return strings.Compare(awsSignedReq.Headers[i].Value, awsSignedReq.Headers[j].Value) < 0
|
||||||
|
}
|
||||||
|
return headerCompare < 0
|
||||||
|
})
|
||||||
|
|
||||||
|
result, err := json.Marshal(awsSignedReq)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return url.QueryEscape(string(result)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *awsSubjectProvider) providerType() string {
|
||||||
|
if sp.securityCredentialsProvider != nil {
|
||||||
|
return programmaticProviderType
|
||||||
|
}
|
||||||
|
return awsProviderType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *awsSubjectProvider) getAWSSessionToken(ctx context.Context) (string, error) {
|
||||||
|
if sp.IMDSv2SessionTokenURL == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "PUT", sp.IMDSv2SessionTokenURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
req.Header.Set(awsIMDSv2SessionTTLHeader, awsIMDSv2SessionTTL)
|
||||||
|
|
||||||
|
sp.logger.DebugContext(ctx, "aws session token request", "request", internallog.HTTPRequest(req, nil))
|
||||||
|
resp, body, err := internal.DoRequest(sp.Client, req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
sp.logger.DebugContext(ctx, "aws session token response", "response", internallog.HTTPResponse(resp, body))
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return "", fmt.Errorf("credentials: unable to retrieve AWS session token: %s", body)
|
||||||
|
}
|
||||||
|
return string(body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *awsSubjectProvider) getRegion(ctx context.Context, headers map[string]string) (string, error) {
|
||||||
|
if sp.securityCredentialsProvider != nil {
|
||||||
|
return sp.securityCredentialsProvider.AwsRegion(ctx, sp.reqOpts)
|
||||||
|
}
|
||||||
|
if canRetrieveRegionFromEnvironment() {
|
||||||
|
if envAwsRegion := getenv(awsRegionEnvVar); envAwsRegion != "" {
|
||||||
|
return envAwsRegion, nil
|
||||||
|
}
|
||||||
|
return getenv(awsDefaultRegionEnvVar), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if sp.RegionURL == "" {
|
||||||
|
return "", errors.New("credentials: unable to determine AWS region")
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", sp.RegionURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, value := range headers {
|
||||||
|
req.Header.Add(name, value)
|
||||||
|
}
|
||||||
|
sp.logger.DebugContext(ctx, "aws region request", "request", internallog.HTTPRequest(req, nil))
|
||||||
|
resp, body, err := internal.DoRequest(sp.Client, req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
sp.logger.DebugContext(ctx, "aws region response", "response", internallog.HTTPResponse(resp, body))
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return "", fmt.Errorf("credentials: unable to retrieve AWS region - %s", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This endpoint will return the region in format: us-east-2b.
|
||||||
|
// Only the us-east-2 part should be used.
|
||||||
|
bodyLen := len(body)
|
||||||
|
if bodyLen == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return string(body[:bodyLen-1]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *awsSubjectProvider) getSecurityCredentials(ctx context.Context, headers map[string]string) (result *AwsSecurityCredentials, err error) {
|
||||||
|
if sp.securityCredentialsProvider != nil {
|
||||||
|
return sp.securityCredentialsProvider.AwsSecurityCredentials(ctx, sp.reqOpts)
|
||||||
|
}
|
||||||
|
if canRetrieveSecurityCredentialFromEnvironment() {
|
||||||
|
return &AwsSecurityCredentials{
|
||||||
|
AccessKeyID: getenv(awsAccessKeyIDEnvVar),
|
||||||
|
SecretAccessKey: getenv(awsSecretAccessKeyEnvVar),
|
||||||
|
SessionToken: getenv(awsSessionTokenEnvVar),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
roleName, err := sp.getMetadataRoleName(ctx, headers)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
credentials, err := sp.getMetadataSecurityCredentials(ctx, roleName, headers)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if credentials.AccessKeyID == "" {
|
||||||
|
return result, errors.New("credentials: missing AccessKeyId credential")
|
||||||
|
}
|
||||||
|
if credentials.SecretAccessKey == "" {
|
||||||
|
return result, errors.New("credentials: missing SecretAccessKey credential")
|
||||||
|
}
|
||||||
|
|
||||||
|
return credentials, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *awsSubjectProvider) getMetadataSecurityCredentials(ctx context.Context, roleName string, headers map[string]string) (*AwsSecurityCredentials, error) {
|
||||||
|
var result *AwsSecurityCredentials
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/%s", sp.CredVerificationURL, roleName), nil)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
for name, value := range headers {
|
||||||
|
req.Header.Add(name, value)
|
||||||
|
}
|
||||||
|
sp.logger.DebugContext(ctx, "aws security credential request", "request", internallog.HTTPRequest(req, nil))
|
||||||
|
resp, body, err := internal.DoRequest(sp.Client, req)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
sp.logger.DebugContext(ctx, "aws security credential response", "response", internallog.HTTPResponse(resp, body))
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return result, fmt.Errorf("credentials: unable to retrieve AWS security credentials - %s", body)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *awsSubjectProvider) getMetadataRoleName(ctx context.Context, headers map[string]string) (string, error) {
|
||||||
|
if sp.CredVerificationURL == "" {
|
||||||
|
return "", errors.New("credentials: unable to determine the AWS metadata server security credentials endpoint")
|
||||||
|
}
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", sp.CredVerificationURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
for name, value := range headers {
|
||||||
|
req.Header.Add(name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
sp.logger.DebugContext(ctx, "aws metadata role request", "request", internallog.HTTPRequest(req, nil))
|
||||||
|
resp, body, err := internal.DoRequest(sp.Client, req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
sp.logger.DebugContext(ctx, "aws metadata role response", "response", internallog.HTTPResponse(resp, body))
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return "", fmt.Errorf("credentials: unable to retrieve AWS role name - %s", body)
|
||||||
|
}
|
||||||
|
return string(body), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// awsRequestSigner is a utility class to sign http requests using a AWS V4 signature.
|
||||||
|
type awsRequestSigner struct {
|
||||||
|
RegionName string
|
||||||
|
AwsSecurityCredentials *AwsSecurityCredentials
|
||||||
|
}
|
||||||
|
|
||||||
|
// signRequest adds the appropriate headers to an http.Request
|
||||||
|
// or returns an error if something prevented this.
|
||||||
|
func (rs *awsRequestSigner) signRequest(req *http.Request) error {
|
||||||
|
// req is assumed non-nil
|
||||||
|
signedRequest := cloneRequest(req)
|
||||||
|
timestamp := Now()
|
||||||
|
signedRequest.Header.Set("host", requestHost(req))
|
||||||
|
if rs.AwsSecurityCredentials.SessionToken != "" {
|
||||||
|
signedRequest.Header.Set(awsSecurityTokenHeader, rs.AwsSecurityCredentials.SessionToken)
|
||||||
|
}
|
||||||
|
if signedRequest.Header.Get("date") == "" {
|
||||||
|
signedRequest.Header.Set(awsDateHeader, timestamp.Format(awsTimeFormatLong))
|
||||||
|
}
|
||||||
|
authorizationCode, err := rs.generateAuthentication(signedRequest, timestamp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
signedRequest.Header.Set("Authorization", authorizationCode)
|
||||||
|
req.Header = signedRequest.Header
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *awsRequestSigner) generateAuthentication(req *http.Request, timestamp time.Time) (string, error) {
|
||||||
|
canonicalHeaderColumns, canonicalHeaderData := canonicalHeaders(req)
|
||||||
|
dateStamp := timestamp.Format(awsTimeFormatShort)
|
||||||
|
serviceName := ""
|
||||||
|
|
||||||
|
if splitHost := strings.Split(requestHost(req), "."); len(splitHost) > 0 {
|
||||||
|
serviceName = splitHost[0]
|
||||||
|
}
|
||||||
|
credentialScope := strings.Join([]string{dateStamp, rs.RegionName, serviceName, awsRequestType}, "/")
|
||||||
|
requestString, err := canonicalRequest(req, canonicalHeaderColumns, canonicalHeaderData)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
requestHash, err := getSha256([]byte(requestString))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
stringToSign := strings.Join([]string{awsAlgorithm, timestamp.Format(awsTimeFormatLong), credentialScope, requestHash}, "\n")
|
||||||
|
signingKey := []byte("AWS4" + rs.AwsSecurityCredentials.SecretAccessKey)
|
||||||
|
for _, signingInput := range []string{
|
||||||
|
dateStamp, rs.RegionName, serviceName, awsRequestType, stringToSign,
|
||||||
|
} {
|
||||||
|
signingKey, err = getHmacSha256(signingKey, []byte(signingInput))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s", awsAlgorithm, rs.AwsSecurityCredentials.AccessKeyID, credentialScope, canonicalHeaderColumns, hex.EncodeToString(signingKey)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSha256(input []byte) (string, error) {
|
||||||
|
hash := sha256.New()
|
||||||
|
if _, err := hash.Write(input); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(hash.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHmacSha256(key, input []byte) ([]byte, error) {
|
||||||
|
hash := hmac.New(sha256.New, key)
|
||||||
|
if _, err := hash.Write(input); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return hash.Sum(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneRequest(r *http.Request) *http.Request {
|
||||||
|
r2 := new(http.Request)
|
||||||
|
*r2 = *r
|
||||||
|
if r.Header != nil {
|
||||||
|
r2.Header = make(http.Header, len(r.Header))
|
||||||
|
|
||||||
|
// Find total number of values.
|
||||||
|
headerCount := 0
|
||||||
|
for _, headerValues := range r.Header {
|
||||||
|
headerCount += len(headerValues)
|
||||||
|
}
|
||||||
|
copiedHeaders := make([]string, headerCount) // shared backing array for headers' values
|
||||||
|
|
||||||
|
for headerKey, headerValues := range r.Header {
|
||||||
|
headerCount = copy(copiedHeaders, headerValues)
|
||||||
|
r2.Header[headerKey] = copiedHeaders[:headerCount:headerCount]
|
||||||
|
copiedHeaders = copiedHeaders[headerCount:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r2
|
||||||
|
}
|
||||||
|
|
||||||
|
func canonicalPath(req *http.Request) string {
|
||||||
|
result := req.URL.EscapedPath()
|
||||||
|
if result == "" {
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
return path.Clean(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func canonicalQuery(req *http.Request) string {
|
||||||
|
queryValues := req.URL.Query()
|
||||||
|
for queryKey := range queryValues {
|
||||||
|
sort.Strings(queryValues[queryKey])
|
||||||
|
}
|
||||||
|
return queryValues.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
func canonicalHeaders(req *http.Request) (string, string) {
|
||||||
|
// Header keys need to be sorted alphabetically.
|
||||||
|
var headers []string
|
||||||
|
lowerCaseHeaders := make(http.Header)
|
||||||
|
for k, v := range req.Header {
|
||||||
|
k := strings.ToLower(k)
|
||||||
|
if _, ok := lowerCaseHeaders[k]; ok {
|
||||||
|
// include additional values
|
||||||
|
lowerCaseHeaders[k] = append(lowerCaseHeaders[k], v...)
|
||||||
|
} else {
|
||||||
|
headers = append(headers, k)
|
||||||
|
lowerCaseHeaders[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(headers)
|
||||||
|
|
||||||
|
var fullHeaders bytes.Buffer
|
||||||
|
for _, header := range headers {
|
||||||
|
headerValue := strings.Join(lowerCaseHeaders[header], ",")
|
||||||
|
fullHeaders.WriteString(header)
|
||||||
|
fullHeaders.WriteRune(':')
|
||||||
|
fullHeaders.WriteString(headerValue)
|
||||||
|
fullHeaders.WriteRune('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(headers, ";"), fullHeaders.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestDataHash(req *http.Request) (string, error) {
|
||||||
|
var requestData []byte
|
||||||
|
if req.Body != nil {
|
||||||
|
requestBody, err := req.GetBody()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer requestBody.Close()
|
||||||
|
|
||||||
|
requestData, err = internal.ReadAll(requestBody)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSha256(requestData)
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestHost(req *http.Request) string {
|
||||||
|
if req.Host != "" {
|
||||||
|
return req.Host
|
||||||
|
}
|
||||||
|
return req.URL.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
func canonicalRequest(req *http.Request, canonicalHeaderColumns, canonicalHeaderData string) (string, error) {
|
||||||
|
dataHash, err := requestDataHash(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", req.Method, canonicalPath(req), canonicalQuery(req), canonicalHeaderData, canonicalHeaderColumns, dataHash), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type awsRequestHeader struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type awsRequest struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Headers []awsRequestHeader `json:"headers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// The AWS region can be provided through AWS_REGION or AWS_DEFAULT_REGION. Only one is
|
||||||
|
// required.
|
||||||
|
func canRetrieveRegionFromEnvironment() bool {
|
||||||
|
return getenv(awsRegionEnvVar) != "" || getenv(awsDefaultRegionEnvVar) != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are available.
|
||||||
|
func canRetrieveSecurityCredentialFromEnvironment() bool {
|
||||||
|
return getenv(awsAccessKeyIDEnvVar) != "" && getenv(awsSecretAccessKeyEnvVar) != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *awsSubjectProvider) shouldUseMetadataServer() bool {
|
||||||
|
return sp.securityCredentialsProvider == nil && (!canRetrieveRegionFromEnvironment() || !canRetrieveSecurityCredentialFromEnvironment())
|
||||||
|
}
|
||||||
284
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/executable_provider.go
generated
vendored
Normal file
284
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/executable_provider.go
generated
vendored
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package externalaccount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
executableSupportedMaxVersion = 1
|
||||||
|
executableDefaultTimeout = 30 * time.Second
|
||||||
|
executableSource = "response"
|
||||||
|
executableProviderType = "executable"
|
||||||
|
outputFileSource = "output file"
|
||||||
|
|
||||||
|
allowExecutablesEnvVar = "GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES"
|
||||||
|
|
||||||
|
jwtTokenType = "urn:ietf:params:oauth:token-type:jwt"
|
||||||
|
idTokenType = "urn:ietf:params:oauth:token-type:id_token"
|
||||||
|
saml2TokenType = "urn:ietf:params:oauth:token-type:saml2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
serviceAccountImpersonationRE = regexp.MustCompile(`https://iamcredentials..+/v1/projects/-/serviceAccounts/(.*@.*):generateAccessToken`)
|
||||||
|
)
|
||||||
|
|
||||||
|
type nonCacheableError struct {
|
||||||
|
message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nce nonCacheableError) Error() string {
|
||||||
|
return nce.message
|
||||||
|
}
|
||||||
|
|
||||||
|
// environment is a contract for testing
|
||||||
|
type environment interface {
|
||||||
|
existingEnv() []string
|
||||||
|
getenv(string) string
|
||||||
|
run(ctx context.Context, command string, env []string) ([]byte, error)
|
||||||
|
now() time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type runtimeEnvironment struct{}
|
||||||
|
|
||||||
|
func (r runtimeEnvironment) existingEnv() []string {
|
||||||
|
return os.Environ()
|
||||||
|
}
|
||||||
|
func (r runtimeEnvironment) getenv(key string) string {
|
||||||
|
return os.Getenv(key)
|
||||||
|
}
|
||||||
|
func (r runtimeEnvironment) now() time.Time {
|
||||||
|
return time.Now().UTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r runtimeEnvironment) run(ctx context.Context, command string, env []string) ([]byte, error) {
|
||||||
|
splitCommand := strings.Fields(command)
|
||||||
|
cmd := exec.CommandContext(ctx, splitCommand[0], splitCommand[1:]...)
|
||||||
|
cmd.Env = env
|
||||||
|
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
return nil, context.DeadlineExceeded
|
||||||
|
}
|
||||||
|
if exitError, ok := err.(*exec.ExitError); ok {
|
||||||
|
return nil, exitCodeError(exitError)
|
||||||
|
}
|
||||||
|
return nil, executableError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesStdout := bytes.TrimSpace(stdout.Bytes())
|
||||||
|
if len(bytesStdout) > 0 {
|
||||||
|
return bytesStdout, nil
|
||||||
|
}
|
||||||
|
return bytes.TrimSpace(stderr.Bytes()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type executableSubjectProvider struct {
|
||||||
|
Command string
|
||||||
|
Timeout time.Duration
|
||||||
|
OutputFile string
|
||||||
|
client *http.Client
|
||||||
|
opts *Options
|
||||||
|
env environment
|
||||||
|
}
|
||||||
|
|
||||||
|
type executableResponse struct {
|
||||||
|
Version int `json:"version,omitempty"`
|
||||||
|
Success *bool `json:"success,omitempty"`
|
||||||
|
TokenType string `json:"token_type,omitempty"`
|
||||||
|
ExpirationTime int64 `json:"expiration_time,omitempty"`
|
||||||
|
IDToken string `json:"id_token,omitempty"`
|
||||||
|
SamlResponse string `json:"saml_response,omitempty"`
|
||||||
|
Code string `json:"code,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *executableSubjectProvider) parseSubjectTokenFromSource(response []byte, source string, now int64) (string, error) {
|
||||||
|
var result executableResponse
|
||||||
|
if err := json.Unmarshal(response, &result); err != nil {
|
||||||
|
return "", jsonParsingError(source, string(response))
|
||||||
|
}
|
||||||
|
// Validate
|
||||||
|
if result.Version == 0 {
|
||||||
|
return "", missingFieldError(source, "version")
|
||||||
|
}
|
||||||
|
if result.Success == nil {
|
||||||
|
return "", missingFieldError(source, "success")
|
||||||
|
}
|
||||||
|
if !*result.Success {
|
||||||
|
if result.Code == "" || result.Message == "" {
|
||||||
|
return "", malformedFailureError()
|
||||||
|
}
|
||||||
|
return "", userDefinedError(result.Code, result.Message)
|
||||||
|
}
|
||||||
|
if result.Version > executableSupportedMaxVersion || result.Version < 0 {
|
||||||
|
return "", unsupportedVersionError(source, result.Version)
|
||||||
|
}
|
||||||
|
if result.ExpirationTime == 0 && sp.OutputFile != "" {
|
||||||
|
return "", missingFieldError(source, "expiration_time")
|
||||||
|
}
|
||||||
|
if result.TokenType == "" {
|
||||||
|
return "", missingFieldError(source, "token_type")
|
||||||
|
}
|
||||||
|
if result.ExpirationTime != 0 && result.ExpirationTime < now {
|
||||||
|
return "", tokenExpiredError()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch result.TokenType {
|
||||||
|
case jwtTokenType, idTokenType:
|
||||||
|
if result.IDToken == "" {
|
||||||
|
return "", missingFieldError(source, "id_token")
|
||||||
|
}
|
||||||
|
return result.IDToken, nil
|
||||||
|
case saml2TokenType:
|
||||||
|
if result.SamlResponse == "" {
|
||||||
|
return "", missingFieldError(source, "saml_response")
|
||||||
|
}
|
||||||
|
return result.SamlResponse, nil
|
||||||
|
default:
|
||||||
|
return "", tokenTypeError(source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *executableSubjectProvider) subjectToken(ctx context.Context) (string, error) {
|
||||||
|
if token, err := sp.getTokenFromOutputFile(); token != "" || err != nil {
|
||||||
|
return token, err
|
||||||
|
}
|
||||||
|
return sp.getTokenFromExecutableCommand(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *executableSubjectProvider) providerType() string {
|
||||||
|
return executableProviderType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *executableSubjectProvider) getTokenFromOutputFile() (token string, err error) {
|
||||||
|
if sp.OutputFile == "" {
|
||||||
|
// This ExecutableCredentialSource doesn't use an OutputFile.
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(sp.OutputFile)
|
||||||
|
if err != nil {
|
||||||
|
// No OutputFile found. Hasn't been created yet, so skip it.
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
data, err := internal.ReadAll(file)
|
||||||
|
if err != nil || len(data) == 0 {
|
||||||
|
// Cachefile exists, but no data found. Get new credential.
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err = sp.parseSubjectTokenFromSource(data, outputFileSource, sp.env.now().Unix())
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(nonCacheableError); ok {
|
||||||
|
// If the cached token is expired we need a new token,
|
||||||
|
// and if the cache contains a failure, we need to try again.
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// There was an error in the cached token, and the developer should be aware of it.
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// Token parsing succeeded. Use found token.
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *executableSubjectProvider) executableEnvironment() []string {
|
||||||
|
result := sp.env.existingEnv()
|
||||||
|
result = append(result, fmt.Sprintf("GOOGLE_EXTERNAL_ACCOUNT_AUDIENCE=%v", sp.opts.Audience))
|
||||||
|
result = append(result, fmt.Sprintf("GOOGLE_EXTERNAL_ACCOUNT_TOKEN_TYPE=%v", sp.opts.SubjectTokenType))
|
||||||
|
result = append(result, "GOOGLE_EXTERNAL_ACCOUNT_INTERACTIVE=0")
|
||||||
|
if sp.opts.ServiceAccountImpersonationURL != "" {
|
||||||
|
matches := serviceAccountImpersonationRE.FindStringSubmatch(sp.opts.ServiceAccountImpersonationURL)
|
||||||
|
if matches != nil {
|
||||||
|
result = append(result, fmt.Sprintf("GOOGLE_EXTERNAL_ACCOUNT_IMPERSONATED_EMAIL=%v", matches[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sp.OutputFile != "" {
|
||||||
|
result = append(result, fmt.Sprintf("GOOGLE_EXTERNAL_ACCOUNT_OUTPUT_FILE=%v", sp.OutputFile))
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *executableSubjectProvider) getTokenFromExecutableCommand(ctx context.Context) (string, error) {
|
||||||
|
// For security reasons, we need our consumers to set this environment variable to allow executables to be run.
|
||||||
|
if sp.env.getenv(allowExecutablesEnvVar) != "1" {
|
||||||
|
return "", errors.New("credentials: executables need to be explicitly allowed (set GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES to '1') to run")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithDeadline(ctx, sp.env.now().Add(sp.Timeout))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
output, err := sp.env.run(ctx, sp.Command, sp.executableEnvironment())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return sp.parseSubjectTokenFromSource(output, executableSource, sp.env.now().Unix())
|
||||||
|
}
|
||||||
|
|
||||||
|
func missingFieldError(source, field string) error {
|
||||||
|
return fmt.Errorf("credentials: %q missing %q field", source, field)
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonParsingError(source, data string) error {
|
||||||
|
return fmt.Errorf("credentials: unable to parse %q: %v", source, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func malformedFailureError() error {
|
||||||
|
return nonCacheableError{"credentials: response must include `error` and `message` fields when unsuccessful"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func userDefinedError(code, message string) error {
|
||||||
|
return nonCacheableError{fmt.Sprintf("credentials: response contains unsuccessful response: (%v) %v", code, message)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unsupportedVersionError(source string, version int) error {
|
||||||
|
return fmt.Errorf("credentials: %v contains unsupported version: %v", source, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tokenExpiredError() error {
|
||||||
|
return nonCacheableError{"credentials: the token returned by the executable is expired"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tokenTypeError(source string) error {
|
||||||
|
return fmt.Errorf("credentials: %v contains unsupported token type", source)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exitCodeError(err *exec.ExitError) error {
|
||||||
|
return fmt.Errorf("credentials: executable command failed with exit code %v: %w", err.ExitCode(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func executableError(err error) error {
|
||||||
|
return fmt.Errorf("credentials: executable command failed: %w", err)
|
||||||
|
}
|
||||||
431
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/externalaccount.go
generated
vendored
Normal file
431
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/externalaccount.go
generated
vendored
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package externalaccount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"cloud.google.com/go/auth/credentials/internal/impersonate"
|
||||||
|
"cloud.google.com/go/auth/credentials/internal/stsexchange"
|
||||||
|
"cloud.google.com/go/auth/internal/credsfile"
|
||||||
|
"github.com/googleapis/gax-go/v2/internallog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
timeoutMinimum = 5 * time.Second
|
||||||
|
timeoutMaximum = 120 * time.Second
|
||||||
|
|
||||||
|
universeDomainPlaceholder = "UNIVERSE_DOMAIN"
|
||||||
|
defaultTokenURL = "https://sts.UNIVERSE_DOMAIN/v1/token"
|
||||||
|
defaultUniverseDomain = "googleapis.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Now aliases time.Now for testing
|
||||||
|
Now = func() time.Time {
|
||||||
|
return time.Now().UTC()
|
||||||
|
}
|
||||||
|
validWorkforceAudiencePattern *regexp.Regexp = regexp.MustCompile(`//iam\.googleapis\.com/locations/[^/]+/workforcePools/`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options stores the configuration for fetching tokens with external credentials.
|
||||||
|
type Options struct {
|
||||||
|
// Audience is the Secure Token Service (STS) audience which contains the resource name for the workload
|
||||||
|
// identity pool or the workforce pool and the provider identifier in that pool.
|
||||||
|
Audience string
|
||||||
|
// SubjectTokenType is the STS token type based on the Oauth2.0 token exchange spec
|
||||||
|
// e.g. `urn:ietf:params:oauth:token-type:jwt`.
|
||||||
|
SubjectTokenType string
|
||||||
|
// TokenURL is the STS token exchange endpoint.
|
||||||
|
TokenURL string
|
||||||
|
// TokenInfoURL is the token_info endpoint used to retrieve the account related information (
|
||||||
|
// user attributes like account identifier, eg. email, username, uid, etc). This is
|
||||||
|
// needed for gCloud session account identification.
|
||||||
|
TokenInfoURL string
|
||||||
|
// ServiceAccountImpersonationURL is the URL for the service account impersonation request. This is only
|
||||||
|
// required for workload identity pools when APIs to be accessed have not integrated with UberMint.
|
||||||
|
ServiceAccountImpersonationURL string
|
||||||
|
// ServiceAccountImpersonationLifetimeSeconds is the number of seconds the service account impersonation
|
||||||
|
// token will be valid for.
|
||||||
|
ServiceAccountImpersonationLifetimeSeconds int
|
||||||
|
// ClientSecret is currently only required if token_info endpoint also
|
||||||
|
// needs to be called with the generated GCP access token. When provided, STS will be
|
||||||
|
// called with additional basic authentication using client_id as username and client_secret as password.
|
||||||
|
ClientSecret string
|
||||||
|
// ClientID is only required in conjunction with ClientSecret, as described above.
|
||||||
|
ClientID string
|
||||||
|
// CredentialSource contains the necessary information to retrieve the token itself, as well
|
||||||
|
// as some environmental information.
|
||||||
|
CredentialSource *credsfile.CredentialSource
|
||||||
|
// QuotaProjectID is injected by gCloud. If the value is non-empty, the Auth libraries
|
||||||
|
// will set the x-goog-user-project which overrides the project associated with the credentials.
|
||||||
|
QuotaProjectID string
|
||||||
|
// Scopes contains the desired scopes for the returned access token.
|
||||||
|
Scopes []string
|
||||||
|
// WorkforcePoolUserProject should be set when it is a workforce pool and
|
||||||
|
// not a workload identity pool. The underlying principal must still have
|
||||||
|
// serviceusage.services.use IAM permission to use the project for
|
||||||
|
// billing/quota. Optional.
|
||||||
|
WorkforcePoolUserProject string
|
||||||
|
// UniverseDomain is the default service domain for a given Cloud universe.
|
||||||
|
// This value will be used in the default STS token URL. The default value
|
||||||
|
// is "googleapis.com". It will not be used if TokenURL is set. Optional.
|
||||||
|
UniverseDomain string
|
||||||
|
// SubjectTokenProvider is an optional token provider for OIDC/SAML
|
||||||
|
// credentials. One of SubjectTokenProvider, AWSSecurityCredentialProvider
|
||||||
|
// or CredentialSource must be provided. Optional.
|
||||||
|
SubjectTokenProvider SubjectTokenProvider
|
||||||
|
// AwsSecurityCredentialsProvider is an AWS Security Credential provider
|
||||||
|
// for AWS credentials. One of SubjectTokenProvider,
|
||||||
|
// AWSSecurityCredentialProvider or CredentialSource must be provided. Optional.
|
||||||
|
AwsSecurityCredentialsProvider AwsSecurityCredentialsProvider
|
||||||
|
// Client for token request.
|
||||||
|
Client *http.Client
|
||||||
|
// IsDefaultClient marks whether the client passed in is a default client that can be overriden.
|
||||||
|
// This is important for X509 credentials which should create a new client if the default was used
|
||||||
|
// but should respect a client explicitly passed in by the user.
|
||||||
|
IsDefaultClient bool
|
||||||
|
// Logger is used for debug logging. If provided, logging will be enabled
|
||||||
|
// at the loggers configured level. By default logging is disabled unless
|
||||||
|
// enabled by setting GOOGLE_SDK_GO_LOGGING_LEVEL in which case a default
|
||||||
|
// logger will be used. Optional.
|
||||||
|
Logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubjectTokenProvider can be used to supply a subject token to exchange for a
|
||||||
|
// GCP access token.
|
||||||
|
type SubjectTokenProvider interface {
|
||||||
|
// SubjectToken should return a valid subject token or an error.
|
||||||
|
// The external account token provider does not cache the returned subject
|
||||||
|
// token, so caching logic should be implemented in the provider to prevent
|
||||||
|
// multiple requests for the same subject token.
|
||||||
|
SubjectToken(ctx context.Context, opts *RequestOptions) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestOptions contains information about the requested subject token or AWS
|
||||||
|
// security credentials from the Google external account credential.
|
||||||
|
type RequestOptions struct {
|
||||||
|
// Audience is the requested audience for the external account credential.
|
||||||
|
Audience string
|
||||||
|
// Subject token type is the requested subject token type for the external
|
||||||
|
// account credential. Expected values include:
|
||||||
|
// “urn:ietf:params:oauth:token-type:jwt”
|
||||||
|
// “urn:ietf:params:oauth:token-type:id-token”
|
||||||
|
// “urn:ietf:params:oauth:token-type:saml2”
|
||||||
|
// “urn:ietf:params:aws:token-type:aws4_request”
|
||||||
|
SubjectTokenType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AwsSecurityCredentialsProvider can be used to supply AwsSecurityCredentials
|
||||||
|
// and an AWS Region to exchange for a GCP access token.
|
||||||
|
type AwsSecurityCredentialsProvider interface {
|
||||||
|
// AwsRegion should return the AWS region or an error.
|
||||||
|
AwsRegion(ctx context.Context, opts *RequestOptions) (string, error)
|
||||||
|
// GetAwsSecurityCredentials should return a valid set of
|
||||||
|
// AwsSecurityCredentials or an error. The external account token provider
|
||||||
|
// does not cache the returned security credentials, so caching logic should
|
||||||
|
// be implemented in the provider to prevent multiple requests for the
|
||||||
|
// same security credentials.
|
||||||
|
AwsSecurityCredentials(ctx context.Context, opts *RequestOptions) (*AwsSecurityCredentials, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AwsSecurityCredentials models AWS security credentials.
|
||||||
|
type AwsSecurityCredentials struct {
|
||||||
|
// AccessKeyId is the AWS Access Key ID - Required.
|
||||||
|
AccessKeyID string `json:"AccessKeyID"`
|
||||||
|
// SecretAccessKey is the AWS Secret Access Key - Required.
|
||||||
|
SecretAccessKey string `json:"SecretAccessKey"`
|
||||||
|
// SessionToken is the AWS Session token. This should be provided for
|
||||||
|
// temporary AWS security credentials - Optional.
|
||||||
|
SessionToken string `json:"Token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) validate() error {
|
||||||
|
if o.Audience == "" {
|
||||||
|
return fmt.Errorf("externalaccount: Audience must be set")
|
||||||
|
}
|
||||||
|
if o.SubjectTokenType == "" {
|
||||||
|
return fmt.Errorf("externalaccount: Subject token type must be set")
|
||||||
|
}
|
||||||
|
if o.WorkforcePoolUserProject != "" {
|
||||||
|
if valid := validWorkforceAudiencePattern.MatchString(o.Audience); !valid {
|
||||||
|
return fmt.Errorf("externalaccount: workforce_pool_user_project should not be set for non-workforce pool credentials")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
count := 0
|
||||||
|
if o.CredentialSource != nil {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if o.SubjectTokenProvider != nil {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if o.AwsSecurityCredentialsProvider != nil {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if count == 0 {
|
||||||
|
return fmt.Errorf("externalaccount: one of CredentialSource, SubjectTokenProvider, or AwsSecurityCredentialsProvider must be set")
|
||||||
|
}
|
||||||
|
if count > 1 {
|
||||||
|
return fmt.Errorf("externalaccount: only one of CredentialSource, SubjectTokenProvider, or AwsSecurityCredentialsProvider must be set")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// client returns the http client that should be used for the token exchange. If a non-default client
|
||||||
|
// is provided, then the client configured in the options will always be returned. If a default client
|
||||||
|
// is provided and the options are configured for X509 credentials, a new client will be created.
|
||||||
|
func (o *Options) client() (*http.Client, error) {
|
||||||
|
// If a client was provided and no override certificate config location was provided, use the provided client.
|
||||||
|
if o.CredentialSource == nil || o.CredentialSource.Certificate == nil || (!o.IsDefaultClient && o.CredentialSource.Certificate.CertificateConfigLocation == "") {
|
||||||
|
return o.Client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a new client should be created, validate and use the certificate source to create a new mTLS client.
|
||||||
|
cert := o.CredentialSource.Certificate
|
||||||
|
if !cert.UseDefaultCertificateConfig && cert.CertificateConfigLocation == "" {
|
||||||
|
return nil, errors.New("credentials: \"certificate\" object must either specify a certificate_config_location or use_default_certificate_config should be true")
|
||||||
|
}
|
||||||
|
if cert.UseDefaultCertificateConfig && cert.CertificateConfigLocation != "" {
|
||||||
|
return nil, errors.New("credentials: \"certificate\" object cannot specify both a certificate_config_location and use_default_certificate_config=true")
|
||||||
|
}
|
||||||
|
return createX509Client(cert.CertificateConfigLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveTokenURL sets the default STS token endpoint with the configured
|
||||||
|
// universe domain.
|
||||||
|
func (o *Options) resolveTokenURL() {
|
||||||
|
if o.TokenURL != "" {
|
||||||
|
return
|
||||||
|
} else if o.UniverseDomain != "" {
|
||||||
|
o.TokenURL = strings.Replace(defaultTokenURL, universeDomainPlaceholder, o.UniverseDomain, 1)
|
||||||
|
} else {
|
||||||
|
o.TokenURL = strings.Replace(defaultTokenURL, universeDomainPlaceholder, defaultUniverseDomain, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTokenProvider returns a [cloud.google.com/go/auth.TokenProvider]
|
||||||
|
// configured with the provided options.
|
||||||
|
func NewTokenProvider(opts *Options) (auth.TokenProvider, error) {
|
||||||
|
if err := opts.validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts.resolveTokenURL()
|
||||||
|
logger := internallog.New(opts.Logger)
|
||||||
|
stp, err := newSubjectTokenProvider(opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := opts.client()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tp := &tokenProvider{
|
||||||
|
client: client,
|
||||||
|
opts: opts,
|
||||||
|
stp: stp,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.ServiceAccountImpersonationURL == "" {
|
||||||
|
return auth.NewCachedTokenProvider(tp, nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
scopes := make([]string, len(opts.Scopes))
|
||||||
|
copy(scopes, opts.Scopes)
|
||||||
|
// needed for impersonation
|
||||||
|
tp.opts.Scopes = []string{"https://www.googleapis.com/auth/cloud-platform"}
|
||||||
|
imp, err := impersonate.NewTokenProvider(&impersonate.Options{
|
||||||
|
Client: client,
|
||||||
|
URL: opts.ServiceAccountImpersonationURL,
|
||||||
|
Scopes: scopes,
|
||||||
|
Tp: auth.NewCachedTokenProvider(tp, nil),
|
||||||
|
TokenLifetimeSeconds: opts.ServiceAccountImpersonationLifetimeSeconds,
|
||||||
|
Logger: logger,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return auth.NewCachedTokenProvider(imp, nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type subjectTokenProvider interface {
|
||||||
|
subjectToken(ctx context.Context) (string, error)
|
||||||
|
providerType() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// tokenProvider is the provider that handles external credentials. It is used to retrieve Tokens.
|
||||||
|
type tokenProvider struct {
|
||||||
|
client *http.Client
|
||||||
|
logger *slog.Logger
|
||||||
|
opts *Options
|
||||||
|
stp subjectTokenProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tp *tokenProvider) Token(ctx context.Context) (*auth.Token, error) {
|
||||||
|
subjectToken, err := tp.stp.subjectToken(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stsRequest := &stsexchange.TokenRequest{
|
||||||
|
GrantType: stsexchange.GrantType,
|
||||||
|
Audience: tp.opts.Audience,
|
||||||
|
Scope: tp.opts.Scopes,
|
||||||
|
RequestedTokenType: stsexchange.TokenType,
|
||||||
|
SubjectToken: subjectToken,
|
||||||
|
SubjectTokenType: tp.opts.SubjectTokenType,
|
||||||
|
}
|
||||||
|
header := make(http.Header)
|
||||||
|
header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
header.Add("x-goog-api-client", getGoogHeaderValue(tp.opts, tp.stp))
|
||||||
|
clientAuth := stsexchange.ClientAuthentication{
|
||||||
|
AuthStyle: auth.StyleInHeader,
|
||||||
|
ClientID: tp.opts.ClientID,
|
||||||
|
ClientSecret: tp.opts.ClientSecret,
|
||||||
|
}
|
||||||
|
var options map[string]interface{}
|
||||||
|
// Do not pass workforce_pool_user_project when client authentication is used.
|
||||||
|
// The client ID is sufficient for determining the user project.
|
||||||
|
if tp.opts.WorkforcePoolUserProject != "" && tp.opts.ClientID == "" {
|
||||||
|
options = map[string]interface{}{
|
||||||
|
"userProject": tp.opts.WorkforcePoolUserProject,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stsResp, err := stsexchange.ExchangeToken(ctx, &stsexchange.Options{
|
||||||
|
Client: tp.client,
|
||||||
|
Endpoint: tp.opts.TokenURL,
|
||||||
|
Request: stsRequest,
|
||||||
|
Authentication: clientAuth,
|
||||||
|
Headers: header,
|
||||||
|
ExtraOpts: options,
|
||||||
|
Logger: tp.logger,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tok := &auth.Token{
|
||||||
|
Value: stsResp.AccessToken,
|
||||||
|
Type: stsResp.TokenType,
|
||||||
|
}
|
||||||
|
// The RFC8693 doesn't define the explicit 0 of "expires_in" field behavior.
|
||||||
|
if stsResp.ExpiresIn <= 0 {
|
||||||
|
return nil, fmt.Errorf("credentials: got invalid expiry from security token service")
|
||||||
|
}
|
||||||
|
tok.Expiry = Now().Add(time.Duration(stsResp.ExpiresIn) * time.Second)
|
||||||
|
return tok, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSubjectTokenProvider determines the type of credsfile.CredentialSource needed to create a
|
||||||
|
// subjectTokenProvider
|
||||||
|
func newSubjectTokenProvider(o *Options) (subjectTokenProvider, error) {
|
||||||
|
logger := internallog.New(o.Logger)
|
||||||
|
reqOpts := &RequestOptions{Audience: o.Audience, SubjectTokenType: o.SubjectTokenType}
|
||||||
|
if o.AwsSecurityCredentialsProvider != nil {
|
||||||
|
return &awsSubjectProvider{
|
||||||
|
securityCredentialsProvider: o.AwsSecurityCredentialsProvider,
|
||||||
|
TargetResource: o.Audience,
|
||||||
|
reqOpts: reqOpts,
|
||||||
|
logger: logger,
|
||||||
|
}, nil
|
||||||
|
} else if o.SubjectTokenProvider != nil {
|
||||||
|
return &programmaticProvider{stp: o.SubjectTokenProvider, opts: reqOpts}, nil
|
||||||
|
} else if len(o.CredentialSource.EnvironmentID) > 3 && o.CredentialSource.EnvironmentID[:3] == "aws" {
|
||||||
|
if awsVersion, err := strconv.Atoi(o.CredentialSource.EnvironmentID[3:]); err == nil {
|
||||||
|
if awsVersion != 1 {
|
||||||
|
return nil, fmt.Errorf("credentials: aws version '%d' is not supported in the current build", awsVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
awsProvider := &awsSubjectProvider{
|
||||||
|
EnvironmentID: o.CredentialSource.EnvironmentID,
|
||||||
|
RegionURL: o.CredentialSource.RegionURL,
|
||||||
|
RegionalCredVerificationURL: o.CredentialSource.RegionalCredVerificationURL,
|
||||||
|
CredVerificationURL: o.CredentialSource.URL,
|
||||||
|
TargetResource: o.Audience,
|
||||||
|
Client: o.Client,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
if o.CredentialSource.IMDSv2SessionTokenURL != "" {
|
||||||
|
awsProvider.IMDSv2SessionTokenURL = o.CredentialSource.IMDSv2SessionTokenURL
|
||||||
|
}
|
||||||
|
|
||||||
|
return awsProvider, nil
|
||||||
|
}
|
||||||
|
} else if o.CredentialSource.File != "" {
|
||||||
|
return &fileSubjectProvider{File: o.CredentialSource.File, Format: o.CredentialSource.Format}, nil
|
||||||
|
} else if o.CredentialSource.URL != "" {
|
||||||
|
return &urlSubjectProvider{
|
||||||
|
URL: o.CredentialSource.URL,
|
||||||
|
Headers: o.CredentialSource.Headers,
|
||||||
|
Format: o.CredentialSource.Format,
|
||||||
|
Client: o.Client,
|
||||||
|
Logger: logger,
|
||||||
|
}, nil
|
||||||
|
} else if o.CredentialSource.Executable != nil {
|
||||||
|
ec := o.CredentialSource.Executable
|
||||||
|
if ec.Command == "" {
|
||||||
|
return nil, errors.New("credentials: missing `command` field — executable command must be provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
execProvider := &executableSubjectProvider{}
|
||||||
|
execProvider.Command = ec.Command
|
||||||
|
if ec.TimeoutMillis == 0 {
|
||||||
|
execProvider.Timeout = executableDefaultTimeout
|
||||||
|
} else {
|
||||||
|
execProvider.Timeout = time.Duration(ec.TimeoutMillis) * time.Millisecond
|
||||||
|
if execProvider.Timeout < timeoutMinimum || execProvider.Timeout > timeoutMaximum {
|
||||||
|
return nil, fmt.Errorf("credentials: invalid `timeout_millis` field — executable timeout must be between %v and %v seconds", timeoutMinimum.Seconds(), timeoutMaximum.Seconds())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
execProvider.OutputFile = ec.OutputFile
|
||||||
|
execProvider.client = o.Client
|
||||||
|
execProvider.opts = o
|
||||||
|
execProvider.env = runtimeEnvironment{}
|
||||||
|
return execProvider, nil
|
||||||
|
} else if o.CredentialSource.Certificate != nil {
|
||||||
|
cert := o.CredentialSource.Certificate
|
||||||
|
if !cert.UseDefaultCertificateConfig && cert.CertificateConfigLocation == "" {
|
||||||
|
return nil, errors.New("credentials: \"certificate\" object must either specify a certificate_config_location or use_default_certificate_config should be true")
|
||||||
|
}
|
||||||
|
if cert.UseDefaultCertificateConfig && cert.CertificateConfigLocation != "" {
|
||||||
|
return nil, errors.New("credentials: \"certificate\" object cannot specify both a certificate_config_location and use_default_certificate_config=true")
|
||||||
|
}
|
||||||
|
return &x509Provider{
|
||||||
|
TrustChainPath: o.CredentialSource.Certificate.TrustChainPath,
|
||||||
|
ConfigFilePath: o.CredentialSource.Certificate.CertificateConfigLocation,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("credentials: unable to parse credential source")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGoogHeaderValue(conf *Options, p subjectTokenProvider) string {
|
||||||
|
return fmt.Sprintf("gl-go/%s auth/%s google-byoid-sdk source/%s sa-impersonation/%t config-lifetime/%t",
|
||||||
|
goVersion(),
|
||||||
|
"unknown",
|
||||||
|
p.providerType(),
|
||||||
|
conf.ServiceAccountImpersonationURL != "",
|
||||||
|
conf.ServiceAccountImpersonationLifetimeSeconds != 0)
|
||||||
|
}
|
||||||
78
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/file_provider.go
generated
vendored
Normal file
78
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/file_provider.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package externalaccount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"cloud.google.com/go/auth/internal/credsfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
fileProviderType = "file"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileSubjectProvider struct {
|
||||||
|
File string
|
||||||
|
Format *credsfile.Format
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *fileSubjectProvider) subjectToken(context.Context) (string, error) {
|
||||||
|
tokenFile, err := os.Open(sp.File)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("credentials: failed to open credential file %q: %w", sp.File, err)
|
||||||
|
}
|
||||||
|
defer tokenFile.Close()
|
||||||
|
tokenBytes, err := internal.ReadAll(tokenFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("credentials: failed to read credential file: %w", err)
|
||||||
|
}
|
||||||
|
tokenBytes = bytes.TrimSpace(tokenBytes)
|
||||||
|
|
||||||
|
if sp.Format == nil {
|
||||||
|
return string(tokenBytes), nil
|
||||||
|
}
|
||||||
|
switch sp.Format.Type {
|
||||||
|
case fileTypeJSON:
|
||||||
|
jsonData := make(map[string]interface{})
|
||||||
|
err = json.Unmarshal(tokenBytes, &jsonData)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("credentials: failed to unmarshal subject token file: %w", err)
|
||||||
|
}
|
||||||
|
val, ok := jsonData[sp.Format.SubjectTokenFieldName]
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("credentials: provided subject_token_field_name not found in credentials")
|
||||||
|
}
|
||||||
|
token, ok := val.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("credentials: improperly formatted subject token")
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
case fileTypeText:
|
||||||
|
return string(tokenBytes), nil
|
||||||
|
default:
|
||||||
|
return "", errors.New("credentials: invalid credential_source file format type: " + sp.Format.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *fileSubjectProvider) providerType() string {
|
||||||
|
return fileProviderType
|
||||||
|
}
|
||||||
74
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/info.go
generated
vendored
Normal file
74
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/info.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package externalaccount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// version is a package internal global variable for testing purposes.
|
||||||
|
version = runtime.Version
|
||||||
|
)
|
||||||
|
|
||||||
|
// versionUnknown is only used when the runtime version cannot be determined.
|
||||||
|
const versionUnknown = "UNKNOWN"
|
||||||
|
|
||||||
|
// goVersion returns a Go runtime version derived from the runtime environment
|
||||||
|
// that is modified to be suitable for reporting in a header, meaning it has no
|
||||||
|
// whitespace. If it is unable to determine the Go runtime version, it returns
|
||||||
|
// versionUnknown.
|
||||||
|
func goVersion() string {
|
||||||
|
const develPrefix = "devel +"
|
||||||
|
|
||||||
|
s := version()
|
||||||
|
if strings.HasPrefix(s, develPrefix) {
|
||||||
|
s = s[len(develPrefix):]
|
||||||
|
if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
|
||||||
|
s = s[:p]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
} else if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
|
||||||
|
s = s[:p]
|
||||||
|
}
|
||||||
|
|
||||||
|
notSemverRune := func(r rune) bool {
|
||||||
|
return !strings.ContainsRune("0123456789.", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(s, "go1") {
|
||||||
|
s = s[2:]
|
||||||
|
var prerelease string
|
||||||
|
if p := strings.IndexFunc(s, notSemverRune); p >= 0 {
|
||||||
|
s, prerelease = s[:p], s[p:]
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(s, ".") {
|
||||||
|
s += "0"
|
||||||
|
} else if strings.Count(s, ".") < 2 {
|
||||||
|
s += ".0"
|
||||||
|
}
|
||||||
|
if prerelease != "" {
|
||||||
|
// Some release candidates already have a dash in them.
|
||||||
|
if !strings.HasPrefix(prerelease, "-") {
|
||||||
|
prerelease = "-" + prerelease
|
||||||
|
}
|
||||||
|
s += prerelease
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return versionUnknown
|
||||||
|
}
|
||||||
30
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/programmatic_provider.go
generated
vendored
Normal file
30
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/programmatic_provider.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2024 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package externalaccount
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type programmaticProvider struct {
|
||||||
|
opts *RequestOptions
|
||||||
|
stp SubjectTokenProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *programmaticProvider) providerType() string {
|
||||||
|
return programmaticProviderType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *programmaticProvider) subjectToken(ctx context.Context) (string, error) {
|
||||||
|
return pp.stp.SubjectToken(ctx, pp.opts)
|
||||||
|
}
|
||||||
93
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/url_provider.go
generated
vendored
Normal file
93
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/url_provider.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package externalaccount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"cloud.google.com/go/auth/internal/credsfile"
|
||||||
|
"github.com/googleapis/gax-go/v2/internallog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
fileTypeText = "text"
|
||||||
|
fileTypeJSON = "json"
|
||||||
|
urlProviderType = "url"
|
||||||
|
programmaticProviderType = "programmatic"
|
||||||
|
x509ProviderType = "x509"
|
||||||
|
)
|
||||||
|
|
||||||
|
type urlSubjectProvider struct {
|
||||||
|
URL string
|
||||||
|
Headers map[string]string
|
||||||
|
Format *credsfile.Format
|
||||||
|
Client *http.Client
|
||||||
|
Logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *urlSubjectProvider) subjectToken(ctx context.Context) (string, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", sp.URL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("credentials: HTTP request for URL-sourced credential failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, val := range sp.Headers {
|
||||||
|
req.Header.Add(key, val)
|
||||||
|
}
|
||||||
|
sp.Logger.DebugContext(ctx, "url subject token request", "request", internallog.HTTPRequest(req, nil))
|
||||||
|
resp, body, err := internal.DoRequest(sp.Client, req)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("credentials: invalid response when retrieving subject token: %w", err)
|
||||||
|
}
|
||||||
|
sp.Logger.DebugContext(ctx, "url subject token response", "response", internallog.HTTPResponse(resp, body))
|
||||||
|
if c := resp.StatusCode; c < http.StatusOK || c >= http.StatusMultipleChoices {
|
||||||
|
return "", fmt.Errorf("credentials: status code %d: %s", c, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sp.Format == nil {
|
||||||
|
return string(body), nil
|
||||||
|
}
|
||||||
|
switch sp.Format.Type {
|
||||||
|
case "json":
|
||||||
|
jsonData := make(map[string]interface{})
|
||||||
|
err = json.Unmarshal(body, &jsonData)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("credentials: failed to unmarshal subject token file: %w", err)
|
||||||
|
}
|
||||||
|
val, ok := jsonData[sp.Format.SubjectTokenFieldName]
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("credentials: provided subject_token_field_name not found in credentials")
|
||||||
|
}
|
||||||
|
token, ok := val.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", errors.New("credentials: improperly formatted subject token")
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
case fileTypeText:
|
||||||
|
return string(body), nil
|
||||||
|
default:
|
||||||
|
return "", errors.New("credentials: invalid credential_source file format type: " + sp.Format.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sp *urlSubjectProvider) providerType() string {
|
||||||
|
return urlProviderType
|
||||||
|
}
|
||||||
220
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/x509_provider.go
generated
vendored
Normal file
220
vendor/cloud.google.com/go/auth/credentials/internal/externalaccount/x509_provider.go
generated
vendored
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
// Copyright 2024 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package externalaccount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth/internal/transport/cert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// x509Provider implements the subjectTokenProvider type for x509 workload
|
||||||
|
// identity credentials. This provider retrieves and formats a JSON array
|
||||||
|
// containing the leaf certificate and trust chain (if provided) as
|
||||||
|
// base64-encoded strings. This JSON array serves as the subject token for
|
||||||
|
// mTLS authentication.
|
||||||
|
type x509Provider struct {
|
||||||
|
// TrustChainPath is the path to the file containing the trust chain certificates.
|
||||||
|
// The file should contain one or more PEM-encoded certificates.
|
||||||
|
TrustChainPath string
|
||||||
|
// ConfigFilePath is the path to the configuration file containing the path
|
||||||
|
// to the leaf certificate file.
|
||||||
|
ConfigFilePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
const pemCertificateHeader = "-----BEGIN CERTIFICATE-----"
|
||||||
|
|
||||||
|
func (xp *x509Provider) providerType() string {
|
||||||
|
return x509ProviderType
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadLeafCertificate loads and parses the leaf certificate from the specified
|
||||||
|
// configuration file. It retrieves the certificate path from the config file,
|
||||||
|
// reads the certificate file, and parses the certificate data.
|
||||||
|
func loadLeafCertificate(configFilePath string) (*x509.Certificate, error) {
|
||||||
|
// Get the path to the certificate file from the configuration file.
|
||||||
|
path, err := cert.GetCertificatePath(configFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get certificate path from config file: %w", err)
|
||||||
|
}
|
||||||
|
leafCertBytes, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read leaf certificate file: %w", err)
|
||||||
|
}
|
||||||
|
// Parse the certificate bytes.
|
||||||
|
return parseCertificate(leafCertBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodeCert encodes a x509.Certificate to a base64 string.
|
||||||
|
func encodeCert(cert *x509.Certificate) string {
|
||||||
|
// cert.Raw contains the raw DER-encoded certificate. Encode the raw certificate bytes to base64.
|
||||||
|
return base64.StdEncoding.EncodeToString(cert.Raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCertificate parses a PEM-encoded certificate from the given byte slice.
|
||||||
|
func parseCertificate(certData []byte) (*x509.Certificate, error) {
|
||||||
|
if len(certData) == 0 {
|
||||||
|
return nil, errors.New("invalid certificate data: empty input")
|
||||||
|
}
|
||||||
|
// Decode the PEM-encoded data.
|
||||||
|
block, _ := pem.Decode(certData)
|
||||||
|
if block == nil {
|
||||||
|
return nil, errors.New("invalid PEM-encoded certificate data: no PEM block found")
|
||||||
|
}
|
||||||
|
if block.Type != "CERTIFICATE" {
|
||||||
|
return nil, fmt.Errorf("invalid PEM-encoded certificate data: expected CERTIFICATE block type, got %s", block.Type)
|
||||||
|
}
|
||||||
|
// Parse the DER-encoded certificate.
|
||||||
|
certificate, err := x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse certificate: %w", err)
|
||||||
|
}
|
||||||
|
return certificate, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readTrustChain reads a file of PEM-encoded X.509 certificates and returns a slice of parsed certificates.
|
||||||
|
// It splits the file content into PEM certificate blocks and parses each one.
|
||||||
|
func readTrustChain(trustChainPath string) ([]*x509.Certificate, error) {
|
||||||
|
certificateTrustChain := []*x509.Certificate{}
|
||||||
|
|
||||||
|
// If no trust chain path is provided, return an empty slice.
|
||||||
|
if trustChainPath == "" {
|
||||||
|
return certificateTrustChain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the trust chain file.
|
||||||
|
trustChainData, err := os.ReadFile(trustChainPath)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
return nil, fmt.Errorf("trust chain file not found: %w", err)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("failed to read trust chain file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the file content into PEM certificate blocks.
|
||||||
|
certBlocks := strings.Split(string(trustChainData), pemCertificateHeader)
|
||||||
|
|
||||||
|
// Iterate over each certificate block.
|
||||||
|
for _, certBlock := range certBlocks {
|
||||||
|
// Trim whitespace from the block.
|
||||||
|
certBlock = strings.TrimSpace(certBlock)
|
||||||
|
|
||||||
|
if certBlock != "" {
|
||||||
|
// Add the PEM header to the block.
|
||||||
|
certData := pemCertificateHeader + "\n" + certBlock
|
||||||
|
|
||||||
|
// Parse the certificate data.
|
||||||
|
cert, err := parseCertificate([]byte(certData))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing certificate from trust chain file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the certificate to the trust chain.
|
||||||
|
certificateTrustChain = append(certificateTrustChain, cert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificateTrustChain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// subjectToken retrieves the X.509 subject token. It loads the leaf
|
||||||
|
// certificate and, if a trust chain path is configured, the trust chain
|
||||||
|
// certificates. It then constructs a JSON array containing the base64-encoded
|
||||||
|
// leaf certificate and each base64-encoded certificate in the trust chain.
|
||||||
|
// The leaf certificate must be at the top of the trust chain file. This JSON
|
||||||
|
// array is used as the subject token for mTLS authentication.
|
||||||
|
func (xp *x509Provider) subjectToken(context.Context) (string, error) {
|
||||||
|
// Load the leaf certificate.
|
||||||
|
leafCert, err := loadLeafCertificate(xp.ConfigFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to load leaf certificate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the trust chain.
|
||||||
|
trustChain, err := readTrustChain(xp.TrustChainPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read trust chain: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the certificate chain with the leaf certificate.
|
||||||
|
certChain := []string{encodeCert(leafCert)}
|
||||||
|
|
||||||
|
// If there is a trust chain, add certificates to the certificate chain.
|
||||||
|
if len(trustChain) > 0 {
|
||||||
|
firstCert := encodeCert(trustChain[0])
|
||||||
|
|
||||||
|
// If the first certificate in the trust chain is not the same as the leaf certificate, add it to the chain.
|
||||||
|
if firstCert != certChain[0] {
|
||||||
|
certChain = append(certChain, firstCert)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the remaining certificates in the trust chain.
|
||||||
|
for i := 1; i < len(trustChain); i++ {
|
||||||
|
encoded := encodeCert(trustChain[i])
|
||||||
|
|
||||||
|
// Return an error if the current certificate is the same as the leaf certificate.
|
||||||
|
if encoded == certChain[0] {
|
||||||
|
return "", errors.New("the leaf certificate must be at the top of the trust chain file")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the current certificate to the chain.
|
||||||
|
certChain = append(certChain, encoded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the certificate chain to a JSON array of base64-encoded strings.
|
||||||
|
jsonChain, err := json.Marshal(certChain)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to format certificate data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the JSON-formatted certificate chain.
|
||||||
|
return string(jsonChain), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// createX509Client creates a new client that is configured with mTLS, using the
|
||||||
|
// certificate configuration specified in the credential source.
|
||||||
|
func createX509Client(certificateConfigLocation string) (*http.Client, error) {
|
||||||
|
certProvider, err := cert.NewWorkloadX509CertProvider(certificateConfigLocation)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
trans := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
|
||||||
|
trans.TLSClientConfig = &tls.Config{
|
||||||
|
GetClientCertificate: certProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a client with default settings plus the X509 workload cert and key.
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: trans,
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
115
vendor/cloud.google.com/go/auth/credentials/internal/externalaccountuser/externalaccountuser.go
generated
vendored
Normal file
115
vendor/cloud.google.com/go/auth/credentials/internal/externalaccountuser/externalaccountuser.go
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package externalaccountuser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"cloud.google.com/go/auth/credentials/internal/stsexchange"
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"github.com/googleapis/gax-go/v2/internallog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options stores the configuration for fetching tokens with external authorized
|
||||||
|
// user credentials.
|
||||||
|
type Options struct {
|
||||||
|
// Audience is the Secure Token Service (STS) audience which contains the
|
||||||
|
// resource name for the workforce pool and the provider identifier in that
|
||||||
|
// pool.
|
||||||
|
Audience string
|
||||||
|
// RefreshToken is the OAuth 2.0 refresh token.
|
||||||
|
RefreshToken string
|
||||||
|
// TokenURL is the STS token exchange endpoint for refresh.
|
||||||
|
TokenURL string
|
||||||
|
// TokenInfoURL is the STS endpoint URL for token introspection. Optional.
|
||||||
|
TokenInfoURL string
|
||||||
|
// ClientID is only required in conjunction with ClientSecret, as described
|
||||||
|
// below.
|
||||||
|
ClientID string
|
||||||
|
// ClientSecret is currently only required if token_info endpoint also needs
|
||||||
|
// to be called with the generated a cloud access token. When provided, STS
|
||||||
|
// will be called with additional basic authentication using client_id as
|
||||||
|
// username and client_secret as password.
|
||||||
|
ClientSecret string
|
||||||
|
// Scopes contains the desired scopes for the returned access token.
|
||||||
|
Scopes []string
|
||||||
|
|
||||||
|
// Client for token request.
|
||||||
|
Client *http.Client
|
||||||
|
// Logger for logging.
|
||||||
|
Logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Options) validate() bool {
|
||||||
|
return c.ClientID != "" && c.ClientSecret != "" && c.RefreshToken != "" && c.TokenURL != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTokenProvider returns a [cloud.google.com/go/auth.TokenProvider]
|
||||||
|
// configured with the provided options.
|
||||||
|
func NewTokenProvider(opts *Options) (auth.TokenProvider, error) {
|
||||||
|
if !opts.validate() {
|
||||||
|
return nil, errors.New("credentials: invalid external_account_authorized_user configuration")
|
||||||
|
}
|
||||||
|
|
||||||
|
tp := &tokenProvider{
|
||||||
|
o: opts,
|
||||||
|
}
|
||||||
|
return auth.NewCachedTokenProvider(tp, nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenProvider struct {
|
||||||
|
o *Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tp *tokenProvider) Token(ctx context.Context) (*auth.Token, error) {
|
||||||
|
opts := tp.o
|
||||||
|
|
||||||
|
clientAuth := stsexchange.ClientAuthentication{
|
||||||
|
AuthStyle: auth.StyleInHeader,
|
||||||
|
ClientID: opts.ClientID,
|
||||||
|
ClientSecret: opts.ClientSecret,
|
||||||
|
}
|
||||||
|
headers := make(http.Header)
|
||||||
|
headers.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
stsResponse, err := stsexchange.RefreshAccessToken(ctx, &stsexchange.Options{
|
||||||
|
Client: opts.Client,
|
||||||
|
Endpoint: opts.TokenURL,
|
||||||
|
RefreshToken: opts.RefreshToken,
|
||||||
|
Authentication: clientAuth,
|
||||||
|
Headers: headers,
|
||||||
|
Logger: internallog.New(tp.o.Logger),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if stsResponse.ExpiresIn < 0 {
|
||||||
|
return nil, errors.New("credentials: invalid expiry from security token service")
|
||||||
|
}
|
||||||
|
|
||||||
|
// guarded by the wrapping with CachedTokenProvider
|
||||||
|
if stsResponse.RefreshToken != "" {
|
||||||
|
opts.RefreshToken = stsResponse.RefreshToken
|
||||||
|
}
|
||||||
|
return &auth.Token{
|
||||||
|
Value: stsResponse.AccessToken,
|
||||||
|
Expiry: time.Now().UTC().Add(time.Duration(stsResponse.ExpiresIn) * time.Second),
|
||||||
|
Type: internal.TokenTypeBearer,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
191
vendor/cloud.google.com/go/auth/credentials/internal/gdch/gdch.go
generated
vendored
Normal file
191
vendor/cloud.google.com/go/auth/credentials/internal/gdch/gdch.go
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package gdch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"cloud.google.com/go/auth/internal/credsfile"
|
||||||
|
"cloud.google.com/go/auth/internal/jwt"
|
||||||
|
"github.com/googleapis/gax-go/v2/internallog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// GrantType is the grant type for the token request.
|
||||||
|
GrantType = "urn:ietf:params:oauth:token-type:token-exchange"
|
||||||
|
requestTokenType = "urn:ietf:params:oauth:token-type:access_token"
|
||||||
|
subjectTokenType = "urn:k8s:params:oauth:token-type:serviceaccount"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
gdchSupportFormatVersions map[string]bool = map[string]bool{
|
||||||
|
"1": true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options for [NewTokenProvider].
|
||||||
|
type Options struct {
|
||||||
|
STSAudience string
|
||||||
|
Client *http.Client
|
||||||
|
Logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTokenProvider returns a [cloud.google.com/go/auth.TokenProvider] from a
|
||||||
|
// GDCH cred file.
|
||||||
|
func NewTokenProvider(f *credsfile.GDCHServiceAccountFile, o *Options) (auth.TokenProvider, error) {
|
||||||
|
if !gdchSupportFormatVersions[f.FormatVersion] {
|
||||||
|
return nil, fmt.Errorf("credentials: unsupported gdch_service_account format %q", f.FormatVersion)
|
||||||
|
}
|
||||||
|
if o.STSAudience == "" {
|
||||||
|
return nil, errors.New("credentials: STSAudience must be set for the GDCH auth flows")
|
||||||
|
}
|
||||||
|
signer, err := internal.ParseKey([]byte(f.PrivateKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
certPool, err := loadCertPool(f.CertPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tp := gdchProvider{
|
||||||
|
serviceIdentity: fmt.Sprintf("system:serviceaccount:%s:%s", f.Project, f.Name),
|
||||||
|
tokenURL: f.TokenURL,
|
||||||
|
aud: o.STSAudience,
|
||||||
|
signer: signer,
|
||||||
|
pkID: f.PrivateKeyID,
|
||||||
|
certPool: certPool,
|
||||||
|
client: o.Client,
|
||||||
|
logger: internallog.New(o.Logger),
|
||||||
|
}
|
||||||
|
return tp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadCertPool(path string) (*x509.CertPool, error) {
|
||||||
|
pool := x509.NewCertPool()
|
||||||
|
pem, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: failed to read certificate: %w", err)
|
||||||
|
}
|
||||||
|
pool.AppendCertsFromPEM(pem)
|
||||||
|
return pool, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type gdchProvider struct {
|
||||||
|
serviceIdentity string
|
||||||
|
tokenURL string
|
||||||
|
aud string
|
||||||
|
signer crypto.Signer
|
||||||
|
pkID string
|
||||||
|
certPool *x509.CertPool
|
||||||
|
|
||||||
|
client *http.Client
|
||||||
|
logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g gdchProvider) Token(ctx context.Context) (*auth.Token, error) {
|
||||||
|
addCertToTransport(g.client, g.certPool)
|
||||||
|
iat := time.Now()
|
||||||
|
exp := iat.Add(time.Hour)
|
||||||
|
claims := jwt.Claims{
|
||||||
|
Iss: g.serviceIdentity,
|
||||||
|
Sub: g.serviceIdentity,
|
||||||
|
Aud: g.tokenURL,
|
||||||
|
Iat: iat.Unix(),
|
||||||
|
Exp: exp.Unix(),
|
||||||
|
}
|
||||||
|
h := jwt.Header{
|
||||||
|
Algorithm: jwt.HeaderAlgRSA256,
|
||||||
|
Type: jwt.HeaderType,
|
||||||
|
KeyID: string(g.pkID),
|
||||||
|
}
|
||||||
|
payload, err := jwt.EncodeJWS(&h, &claims, g.signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v := url.Values{}
|
||||||
|
v.Set("grant_type", GrantType)
|
||||||
|
v.Set("audience", g.aud)
|
||||||
|
v.Set("requested_token_type", requestTokenType)
|
||||||
|
v.Set("subject_token", payload)
|
||||||
|
v.Set("subject_token_type", subjectTokenType)
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", g.tokenURL, strings.NewReader(v.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
g.logger.DebugContext(ctx, "gdch token request", "request", internallog.HTTPRequest(req, []byte(v.Encode())))
|
||||||
|
resp, body, err := internal.DoRequest(g.client, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: cannot fetch token: %w", err)
|
||||||
|
}
|
||||||
|
g.logger.DebugContext(ctx, "gdch token response", "response", internallog.HTTPResponse(resp, body))
|
||||||
|
if c := resp.StatusCode; c < http.StatusOK || c > http.StatusMultipleChoices {
|
||||||
|
return nil, &auth.Error{
|
||||||
|
Response: resp,
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokenRes struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
ExpiresIn int64 `json:"expires_in"` // relative seconds from now
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(body, &tokenRes); err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: cannot fetch token: %w", err)
|
||||||
|
}
|
||||||
|
token := &auth.Token{
|
||||||
|
Value: tokenRes.AccessToken,
|
||||||
|
Type: tokenRes.TokenType,
|
||||||
|
}
|
||||||
|
raw := make(map[string]interface{})
|
||||||
|
json.Unmarshal(body, &raw) // no error checks for optional fields
|
||||||
|
token.Metadata = raw
|
||||||
|
|
||||||
|
if secs := tokenRes.ExpiresIn; secs > 0 {
|
||||||
|
token.Expiry = time.Now().Add(time.Duration(secs) * time.Second)
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// addCertToTransport makes a best effort attempt at adding in the cert info to
|
||||||
|
// the client. It tries to keep all configured transport settings if the
|
||||||
|
// underlying transport is an http.Transport. Or else it overwrites the
|
||||||
|
// transport with defaults adding in the certs.
|
||||||
|
func addCertToTransport(hc *http.Client, certPool *x509.CertPool) {
|
||||||
|
trans, ok := hc.Transport.(*http.Transport)
|
||||||
|
if !ok {
|
||||||
|
trans = http.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
}
|
||||||
|
trans.TLSClientConfig = &tls.Config{
|
||||||
|
RootCAs: certPool,
|
||||||
|
}
|
||||||
|
}
|
||||||
105
vendor/cloud.google.com/go/auth/credentials/internal/impersonate/idtoken.go
generated
vendored
Normal file
105
vendor/cloud.google.com/go/auth/credentials/internal/impersonate/idtoken.go
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
// Copyright 2025 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package impersonate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"github.com/googleapis/gax-go/v2/internallog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
universeDomainPlaceholder = "UNIVERSE_DOMAIN"
|
||||||
|
iamCredentialsUniverseDomainEndpoint = "https://iamcredentials.UNIVERSE_DOMAIN"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IDTokenIAMOptions provides configuration for [IDTokenIAMOptions.Token].
|
||||||
|
type IDTokenIAMOptions struct {
|
||||||
|
// Client is required.
|
||||||
|
Client *http.Client
|
||||||
|
// Logger is required.
|
||||||
|
Logger *slog.Logger
|
||||||
|
UniverseDomain auth.CredentialsPropertyProvider
|
||||||
|
ServiceAccountEmail string
|
||||||
|
GenerateIDTokenRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateIDTokenRequest holds the request to the IAM generateIdToken RPC.
|
||||||
|
type GenerateIDTokenRequest struct {
|
||||||
|
Audience string `json:"audience"`
|
||||||
|
IncludeEmail bool `json:"includeEmail"`
|
||||||
|
// Delegates are the ordered, fully-qualified resource name for service
|
||||||
|
// accounts in a delegation chain. Each service account must be granted
|
||||||
|
// roles/iam.serviceAccountTokenCreator on the next service account in the
|
||||||
|
// chain. The delegates must have the following format:
|
||||||
|
// projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}. The - wildcard
|
||||||
|
// character is required; replacing it with a project ID is invalid.
|
||||||
|
// Optional.
|
||||||
|
Delegates []string `json:"delegates,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateIDTokenResponse holds the response from the IAM generateIdToken RPC.
|
||||||
|
type GenerateIDTokenResponse struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token call IAM generateIdToken with the configuration provided in [IDTokenIAMOptions].
|
||||||
|
func (o IDTokenIAMOptions) Token(ctx context.Context) (*auth.Token, error) {
|
||||||
|
universeDomain, err := o.UniverseDomain.GetProperty(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
endpoint := strings.Replace(iamCredentialsUniverseDomainEndpoint, universeDomainPlaceholder, universeDomain, 1)
|
||||||
|
url := fmt.Sprintf("%s/v1/%s:generateIdToken", endpoint, internal.FormatIAMServiceAccountResource(o.ServiceAccountEmail))
|
||||||
|
|
||||||
|
bodyBytes, err := json.Marshal(o.GenerateIDTokenRequest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("impersonate: unable to marshal request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(bodyBytes))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("impersonate: unable to create request: %w", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
o.Logger.DebugContext(ctx, "impersonated idtoken request", "request", internallog.HTTPRequest(req, bodyBytes))
|
||||||
|
resp, body, err := internal.DoRequest(o.Client, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("impersonate: unable to generate ID token: %w", err)
|
||||||
|
}
|
||||||
|
o.Logger.DebugContext(ctx, "impersonated idtoken response", "response", internallog.HTTPResponse(resp, body))
|
||||||
|
if c := resp.StatusCode; c < 200 || c > 299 {
|
||||||
|
return nil, fmt.Errorf("impersonate: status code %d: %s", c, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokenResp GenerateIDTokenResponse
|
||||||
|
if err := json.Unmarshal(body, &tokenResp); err != nil {
|
||||||
|
return nil, fmt.Errorf("impersonate: unable to parse response: %w", err)
|
||||||
|
}
|
||||||
|
return &auth.Token{
|
||||||
|
Value: tokenResp.Token,
|
||||||
|
// Generated ID tokens are good for one hour.
|
||||||
|
Expiry: time.Now().Add(1 * time.Hour),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
168
vendor/cloud.google.com/go/auth/credentials/internal/impersonate/impersonate.go
generated
vendored
Normal file
168
vendor/cloud.google.com/go/auth/credentials/internal/impersonate/impersonate.go
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package impersonate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"cloud.google.com/go/auth/internal/transport/headers"
|
||||||
|
"github.com/googleapis/gax-go/v2/internallog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultTokenLifetime = "3600s"
|
||||||
|
authHeaderKey = "Authorization"
|
||||||
|
)
|
||||||
|
|
||||||
|
var serviceAccountEmailRegex = regexp.MustCompile(`serviceAccounts/(.+?):generateAccessToken`)
|
||||||
|
|
||||||
|
// generateAccesstokenReq is used for service account impersonation
|
||||||
|
type generateAccessTokenReq struct {
|
||||||
|
Delegates []string `json:"delegates,omitempty"`
|
||||||
|
Lifetime string `json:"lifetime,omitempty"`
|
||||||
|
Scope []string `json:"scope,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type impersonateTokenResponse struct {
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
ExpireTime string `json:"expireTime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTokenProvider uses a source credential, stored in Ts, to request an access token to the provided URL.
|
||||||
|
// Scopes can be defined when the access token is requested.
|
||||||
|
func NewTokenProvider(opts *Options) (auth.TokenProvider, error) {
|
||||||
|
if err := opts.validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return opts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options for [NewTokenProvider].
|
||||||
|
type Options struct {
|
||||||
|
// Tp is the source credential used to generate a token on the
|
||||||
|
// impersonated service account. Required.
|
||||||
|
Tp auth.TokenProvider
|
||||||
|
|
||||||
|
// URL is the endpoint to call to generate a token
|
||||||
|
// on behalf of the service account. Required.
|
||||||
|
URL string
|
||||||
|
// Scopes that the impersonated credential should have. Required.
|
||||||
|
Scopes []string
|
||||||
|
// Delegates are the service account email addresses in a delegation chain.
|
||||||
|
// Each service account must be granted roles/iam.serviceAccountTokenCreator
|
||||||
|
// on the next service account in the chain. Optional.
|
||||||
|
Delegates []string
|
||||||
|
// TokenLifetimeSeconds is the number of seconds the impersonation token will
|
||||||
|
// be valid for. Defaults to 1 hour if unset. Optional.
|
||||||
|
TokenLifetimeSeconds int
|
||||||
|
// Client configures the underlying client used to make network requests
|
||||||
|
// when fetching tokens. Required.
|
||||||
|
Client *http.Client
|
||||||
|
// Logger is used for debug logging. If provided, logging will be enabled
|
||||||
|
// at the loggers configured level. By default logging is disabled unless
|
||||||
|
// enabled by setting GOOGLE_SDK_GO_LOGGING_LEVEL in which case a default
|
||||||
|
// logger will be used. Optional.
|
||||||
|
Logger *slog.Logger
|
||||||
|
// UniverseDomain is the default service domain for a given Cloud universe.
|
||||||
|
UniverseDomain string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) validate() error {
|
||||||
|
if o.Tp == nil {
|
||||||
|
return errors.New("credentials: missing required 'source_credentials' field in impersonated credentials")
|
||||||
|
}
|
||||||
|
if o.URL == "" {
|
||||||
|
return errors.New("credentials: missing required 'service_account_impersonation_url' field in impersonated credentials")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token performs the exchange to get a temporary service account token to allow access to GCP.
|
||||||
|
func (o *Options) Token(ctx context.Context) (*auth.Token, error) {
|
||||||
|
logger := internallog.New(o.Logger)
|
||||||
|
lifetime := defaultTokenLifetime
|
||||||
|
if o.TokenLifetimeSeconds != 0 {
|
||||||
|
lifetime = fmt.Sprintf("%ds", o.TokenLifetimeSeconds)
|
||||||
|
}
|
||||||
|
reqBody := generateAccessTokenReq{
|
||||||
|
Lifetime: lifetime,
|
||||||
|
Scope: o.Scopes,
|
||||||
|
Delegates: o.Delegates,
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(reqBody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: unable to marshal request: %w", err)
|
||||||
|
}
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", o.URL, bytes.NewReader(b))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: unable to create impersonation request: %w", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
sourceToken, err := o.Tp.Token(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
headers.SetAuthHeader(sourceToken, req)
|
||||||
|
logger.DebugContext(ctx, "impersonated token request", "request", internallog.HTTPRequest(req, b))
|
||||||
|
resp, body, err := internal.DoRequest(o.Client, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: unable to generate access token: %w", err)
|
||||||
|
}
|
||||||
|
logger.DebugContext(ctx, "impersonated token response", "response", internallog.HTTPResponse(resp, body))
|
||||||
|
if c := resp.StatusCode; c < http.StatusOK || c >= http.StatusMultipleChoices {
|
||||||
|
return nil, fmt.Errorf("credentials: status code %d: %s", c, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
var accessTokenResp impersonateTokenResponse
|
||||||
|
if err := json.Unmarshal(body, &accessTokenResp); err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: unable to parse response: %w", err)
|
||||||
|
}
|
||||||
|
expiry, err := time.Parse(time.RFC3339, accessTokenResp.ExpireTime)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: unable to parse expiry: %w", err)
|
||||||
|
}
|
||||||
|
token := &auth.Token{
|
||||||
|
Value: accessTokenResp.AccessToken,
|
||||||
|
Expiry: expiry,
|
||||||
|
Type: internal.TokenTypeBearer,
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractServiceAccountEmail extracts the service account email from the impersonation URL.
|
||||||
|
// The impersonation URL is expected to be in the format:
|
||||||
|
// https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/{SERVICE_ACCOUNT_EMAIL}:generateAccessToken
|
||||||
|
// or
|
||||||
|
// https://iamcredentials.googleapis.com/v1/projects/{PROJECT_ID}/serviceAccounts/{SERVICE_ACCOUNT_EMAIL}:generateAccessToken
|
||||||
|
// Returns an error if the email cannot be extracted.
|
||||||
|
func ExtractServiceAccountEmail(impersonationURL string) (string, error) {
|
||||||
|
matches := serviceAccountEmailRegex.FindStringSubmatch(impersonationURL)
|
||||||
|
|
||||||
|
if len(matches) < 2 {
|
||||||
|
return "", fmt.Errorf("credentials: invalid impersonation URL format: %s", impersonationURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches[1], nil
|
||||||
|
}
|
||||||
167
vendor/cloud.google.com/go/auth/credentials/internal/stsexchange/sts_exchange.go
generated
vendored
Normal file
167
vendor/cloud.google.com/go/auth/credentials/internal/stsexchange/sts_exchange.go
generated
vendored
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package stsexchange
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"github.com/googleapis/gax-go/v2/internallog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// GrantType for a sts exchange.
|
||||||
|
GrantType = "urn:ietf:params:oauth:grant-type:token-exchange"
|
||||||
|
// TokenType for a sts exchange.
|
||||||
|
TokenType = "urn:ietf:params:oauth:token-type:access_token"
|
||||||
|
|
||||||
|
jwtTokenType = "urn:ietf:params:oauth:token-type:jwt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options stores the configuration for making an sts exchange request.
|
||||||
|
type Options struct {
|
||||||
|
Client *http.Client
|
||||||
|
Logger *slog.Logger
|
||||||
|
Endpoint string
|
||||||
|
Request *TokenRequest
|
||||||
|
Authentication ClientAuthentication
|
||||||
|
Headers http.Header
|
||||||
|
// ExtraOpts are optional fields marshalled into the `options` field of the
|
||||||
|
// request body.
|
||||||
|
ExtraOpts map[string]interface{}
|
||||||
|
RefreshToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefreshAccessToken performs the token exchange using a refresh token flow.
|
||||||
|
func RefreshAccessToken(ctx context.Context, opts *Options) (*TokenResponse, error) {
|
||||||
|
data := url.Values{}
|
||||||
|
data.Set("grant_type", "refresh_token")
|
||||||
|
data.Set("refresh_token", opts.RefreshToken)
|
||||||
|
return doRequest(ctx, opts, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExchangeToken performs an oauth2 token exchange with the provided endpoint.
|
||||||
|
func ExchangeToken(ctx context.Context, opts *Options) (*TokenResponse, error) {
|
||||||
|
data := url.Values{}
|
||||||
|
data.Set("audience", opts.Request.Audience)
|
||||||
|
data.Set("grant_type", GrantType)
|
||||||
|
data.Set("requested_token_type", TokenType)
|
||||||
|
data.Set("subject_token_type", opts.Request.SubjectTokenType)
|
||||||
|
data.Set("subject_token", opts.Request.SubjectToken)
|
||||||
|
data.Set("scope", strings.Join(opts.Request.Scope, " "))
|
||||||
|
if opts.ExtraOpts != nil {
|
||||||
|
opts, err := json.Marshal(opts.ExtraOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: failed to marshal additional options: %w", err)
|
||||||
|
}
|
||||||
|
data.Set("options", string(opts))
|
||||||
|
}
|
||||||
|
return doRequest(ctx, opts, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doRequest(ctx context.Context, opts *Options, data url.Values) (*TokenResponse, error) {
|
||||||
|
opts.Authentication.InjectAuthentication(data, opts.Headers)
|
||||||
|
encodedData := data.Encode()
|
||||||
|
logger := internallog.New(opts.Logger)
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", opts.Endpoint, strings.NewReader(encodedData))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: failed to properly build http request: %w", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
for key, list := range opts.Headers {
|
||||||
|
for _, val := range list {
|
||||||
|
req.Header.Add(key, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Length", strconv.Itoa(len(encodedData)))
|
||||||
|
|
||||||
|
logger.DebugContext(ctx, "sts token request", "request", internallog.HTTPRequest(req, []byte(encodedData)))
|
||||||
|
resp, body, err := internal.DoRequest(opts.Client, req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: invalid response from Secure Token Server: %w", err)
|
||||||
|
}
|
||||||
|
logger.DebugContext(ctx, "sts token response", "response", internallog.HTTPResponse(resp, body))
|
||||||
|
if c := resp.StatusCode; c < http.StatusOK || c > http.StatusMultipleChoices {
|
||||||
|
return nil, fmt.Errorf("credentials: status code %d: %s", c, body)
|
||||||
|
}
|
||||||
|
var stsResp TokenResponse
|
||||||
|
if err := json.Unmarshal(body, &stsResp); err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: failed to unmarshal response body from Secure Token Server: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &stsResp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenRequest contains fields necessary to make an oauth2 token
|
||||||
|
// exchange.
|
||||||
|
type TokenRequest struct {
|
||||||
|
ActingParty struct {
|
||||||
|
ActorToken string
|
||||||
|
ActorTokenType string
|
||||||
|
}
|
||||||
|
GrantType string
|
||||||
|
Resource string
|
||||||
|
Audience string
|
||||||
|
Scope []string
|
||||||
|
RequestedTokenType string
|
||||||
|
SubjectToken string
|
||||||
|
SubjectTokenType string
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenResponse is used to decode the remote server response during
|
||||||
|
// an oauth2 token exchange.
|
||||||
|
type TokenResponse struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
IssuedTokenType string `json:"issued_token_type"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
ExpiresIn int `json:"expires_in"`
|
||||||
|
Scope string `json:"scope"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientAuthentication represents an OAuth client ID and secret and the
|
||||||
|
// mechanism for passing these credentials as stated in rfc6749#2.3.1.
|
||||||
|
type ClientAuthentication struct {
|
||||||
|
AuthStyle auth.Style
|
||||||
|
ClientID string
|
||||||
|
ClientSecret string
|
||||||
|
}
|
||||||
|
|
||||||
|
// InjectAuthentication is used to add authentication to a Secure Token Service
|
||||||
|
// exchange request. It modifies either the passed url.Values or http.Header
|
||||||
|
// depending on the desired authentication format.
|
||||||
|
func (c *ClientAuthentication) InjectAuthentication(values url.Values, headers http.Header) {
|
||||||
|
if c.ClientID == "" || c.ClientSecret == "" || values == nil || headers == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch c.AuthStyle {
|
||||||
|
case auth.StyleInHeader:
|
||||||
|
plainHeader := c.ClientID + ":" + c.ClientSecret
|
||||||
|
headers.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(plainHeader)))
|
||||||
|
default:
|
||||||
|
values.Set("client_id", c.ClientID)
|
||||||
|
values.Set("client_secret", c.ClientSecret)
|
||||||
|
}
|
||||||
|
}
|
||||||
89
vendor/cloud.google.com/go/auth/credentials/selfsignedjwt.go
generated
vendored
Normal file
89
vendor/cloud.google.com/go/auth/credentials/selfsignedjwt.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"cloud.google.com/go/auth/internal/credsfile"
|
||||||
|
"cloud.google.com/go/auth/internal/jwt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// for testing
|
||||||
|
now func() time.Time = time.Now
|
||||||
|
)
|
||||||
|
|
||||||
|
// configureSelfSignedJWT uses the private key in the service account to create
|
||||||
|
// a JWT without making a network call.
|
||||||
|
func configureSelfSignedJWT(f *credsfile.ServiceAccountFile, opts *DetectOptions) (auth.TokenProvider, error) {
|
||||||
|
if len(opts.scopes()) == 0 && opts.Audience == "" {
|
||||||
|
return nil, errors.New("credentials: both scopes and audience are empty")
|
||||||
|
}
|
||||||
|
signer, err := internal.ParseKey([]byte(f.PrivateKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: could not parse key: %w", err)
|
||||||
|
}
|
||||||
|
return &selfSignedTokenProvider{
|
||||||
|
email: f.ClientEmail,
|
||||||
|
audience: opts.Audience,
|
||||||
|
scopes: opts.scopes(),
|
||||||
|
signer: signer,
|
||||||
|
pkID: f.PrivateKeyID,
|
||||||
|
logger: opts.logger(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type selfSignedTokenProvider struct {
|
||||||
|
email string
|
||||||
|
audience string
|
||||||
|
scopes []string
|
||||||
|
signer crypto.Signer
|
||||||
|
pkID string
|
||||||
|
logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tp *selfSignedTokenProvider) Token(context.Context) (*auth.Token, error) {
|
||||||
|
iat := now()
|
||||||
|
exp := iat.Add(time.Hour)
|
||||||
|
scope := strings.Join(tp.scopes, " ")
|
||||||
|
c := &jwt.Claims{
|
||||||
|
Iss: tp.email,
|
||||||
|
Sub: tp.email,
|
||||||
|
Aud: tp.audience,
|
||||||
|
Scope: scope,
|
||||||
|
Iat: iat.Unix(),
|
||||||
|
Exp: exp.Unix(),
|
||||||
|
}
|
||||||
|
h := &jwt.Header{
|
||||||
|
Algorithm: jwt.HeaderAlgRSA256,
|
||||||
|
Type: jwt.HeaderType,
|
||||||
|
KeyID: string(tp.pkID),
|
||||||
|
}
|
||||||
|
tok, err := jwt.EncodeJWS(h, c, tp.signer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("credentials: could not encode JWT: %w", err)
|
||||||
|
}
|
||||||
|
tp.logger.Debug("created self-signed JWT", "token", tok)
|
||||||
|
return &auth.Token{Value: tok, Type: internal.TokenTypeBearer, Expiry: exp}, nil
|
||||||
|
}
|
||||||
254
vendor/cloud.google.com/go/auth/httptransport/httptransport.go
generated
vendored
Normal file
254
vendor/cloud.google.com/go/auth/httptransport/httptransport.go
generated
vendored
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package httptransport provides functionality for managing HTTP client
|
||||||
|
// connections to Google Cloud services.
|
||||||
|
package httptransport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
detect "cloud.google.com/go/auth/credentials"
|
||||||
|
"cloud.google.com/go/auth/internal/transport"
|
||||||
|
"cloud.google.com/go/auth/internal/transport/headers"
|
||||||
|
"github.com/googleapis/gax-go/v2/internallog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientCertProvider is a function that returns a TLS client certificate to be
|
||||||
|
// used when opening TLS connections. It follows the same semantics as
|
||||||
|
// [crypto/tls.Config.GetClientCertificate].
|
||||||
|
type ClientCertProvider = func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
|
||||||
|
|
||||||
|
// Options used to configure a [net/http.Client] from [NewClient].
|
||||||
|
type Options struct {
|
||||||
|
// DisableTelemetry disables default telemetry (OpenTelemetry). An example
|
||||||
|
// reason to do so would be to bind custom telemetry that overrides the
|
||||||
|
// defaults.
|
||||||
|
DisableTelemetry bool
|
||||||
|
// DisableAuthentication specifies that no authentication should be used. It
|
||||||
|
// is suitable only for testing and for accessing public resources, like
|
||||||
|
// public Google Cloud Storage buckets.
|
||||||
|
DisableAuthentication bool
|
||||||
|
// Headers are extra HTTP headers that will be appended to every outgoing
|
||||||
|
// request.
|
||||||
|
Headers http.Header
|
||||||
|
// BaseRoundTripper overrides the base transport used for serving requests.
|
||||||
|
// If specified ClientCertProvider is ignored.
|
||||||
|
BaseRoundTripper http.RoundTripper
|
||||||
|
// Endpoint overrides the default endpoint to be used for a service.
|
||||||
|
Endpoint string
|
||||||
|
// APIKey specifies an API key to be used as the basis for authentication.
|
||||||
|
// If set DetectOpts are ignored.
|
||||||
|
APIKey string
|
||||||
|
// Credentials used to add Authorization header to all requests. If set
|
||||||
|
// DetectOpts are ignored.
|
||||||
|
Credentials *auth.Credentials
|
||||||
|
// ClientCertProvider is a function that returns a TLS client certificate to
|
||||||
|
// be used when opening TLS connections. It follows the same semantics as
|
||||||
|
// crypto/tls.Config.GetClientCertificate.
|
||||||
|
ClientCertProvider ClientCertProvider
|
||||||
|
// DetectOpts configures settings for detect Application Default
|
||||||
|
// Credentials.
|
||||||
|
DetectOpts *detect.DetectOptions
|
||||||
|
// UniverseDomain is the default service domain for a given Cloud universe.
|
||||||
|
// The default value is "googleapis.com". This is the universe domain
|
||||||
|
// configured for the client, which will be compared to the universe domain
|
||||||
|
// that is separately configured for the credentials.
|
||||||
|
UniverseDomain string
|
||||||
|
// Logger is used for debug logging. If provided, logging will be enabled
|
||||||
|
// at the loggers configured level. By default logging is disabled unless
|
||||||
|
// enabled by setting GOOGLE_SDK_GO_LOGGING_LEVEL in which case a default
|
||||||
|
// logger will be used. Optional.
|
||||||
|
Logger *slog.Logger
|
||||||
|
|
||||||
|
// InternalOptions are NOT meant to be set directly by consumers of this
|
||||||
|
// package, they should only be set by generated client code.
|
||||||
|
InternalOptions *InternalOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) validate() error {
|
||||||
|
if o == nil {
|
||||||
|
return errors.New("httptransport: opts required to be non-nil")
|
||||||
|
}
|
||||||
|
if o.InternalOptions != nil && o.InternalOptions.SkipValidation {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
hasCreds := o.APIKey != "" ||
|
||||||
|
o.Credentials != nil ||
|
||||||
|
(o.DetectOpts != nil && len(o.DetectOpts.CredentialsJSON) > 0) ||
|
||||||
|
(o.DetectOpts != nil && o.DetectOpts.CredentialsFile != "")
|
||||||
|
if o.DisableAuthentication && hasCreds {
|
||||||
|
return errors.New("httptransport: DisableAuthentication is incompatible with options that set or detect credentials")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// client returns the client a user set for the detect options or nil if one was
|
||||||
|
// not set.
|
||||||
|
func (o *Options) client() *http.Client {
|
||||||
|
if o.DetectOpts != nil && o.DetectOpts.Client != nil {
|
||||||
|
return o.DetectOpts.Client
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) logger() *slog.Logger {
|
||||||
|
return internallog.New(o.Logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) resolveDetectOptions() *detect.DetectOptions {
|
||||||
|
io := o.InternalOptions
|
||||||
|
// soft-clone these so we are not updating a ref the user holds and may reuse
|
||||||
|
do := transport.CloneDetectOptions(o.DetectOpts)
|
||||||
|
|
||||||
|
// If scoped JWTs are enabled user provided an aud, allow self-signed JWT.
|
||||||
|
if (io != nil && io.EnableJWTWithScope) || do.Audience != "" {
|
||||||
|
do.UseSelfSignedJWT = true
|
||||||
|
}
|
||||||
|
// Only default scopes if user did not also set an audience.
|
||||||
|
if len(do.Scopes) == 0 && do.Audience == "" && io != nil && len(io.DefaultScopes) > 0 {
|
||||||
|
do.Scopes = make([]string, len(io.DefaultScopes))
|
||||||
|
copy(do.Scopes, io.DefaultScopes)
|
||||||
|
}
|
||||||
|
if len(do.Scopes) == 0 && do.Audience == "" && io != nil {
|
||||||
|
do.Audience = o.InternalOptions.DefaultAudience
|
||||||
|
}
|
||||||
|
if o.ClientCertProvider != nil {
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
GetClientCertificate: o.ClientCertProvider,
|
||||||
|
}
|
||||||
|
do.Client = transport.DefaultHTTPClientWithTLS(tlsConfig)
|
||||||
|
do.TokenURL = detect.GoogleMTLSTokenURL
|
||||||
|
}
|
||||||
|
if do.Logger == nil {
|
||||||
|
do.Logger = o.logger()
|
||||||
|
}
|
||||||
|
return do
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalOptions are only meant to be set by generated client code. These are
|
||||||
|
// not meant to be set directly by consumers of this package. Configuration in
|
||||||
|
// this type is considered EXPERIMENTAL and may be removed at any time in the
|
||||||
|
// future without warning.
|
||||||
|
type InternalOptions struct {
|
||||||
|
// EnableJWTWithScope specifies if scope can be used with self-signed JWT.
|
||||||
|
EnableJWTWithScope bool
|
||||||
|
// DefaultAudience specifies a default audience to be used as the audience
|
||||||
|
// field ("aud") for the JWT token authentication.
|
||||||
|
DefaultAudience string
|
||||||
|
// DefaultEndpointTemplate combined with UniverseDomain specifies the
|
||||||
|
// default endpoint.
|
||||||
|
DefaultEndpointTemplate string
|
||||||
|
// DefaultMTLSEndpoint specifies the default mTLS endpoint.
|
||||||
|
DefaultMTLSEndpoint string
|
||||||
|
// DefaultScopes specifies the default OAuth2 scopes to be used for a
|
||||||
|
// service.
|
||||||
|
DefaultScopes []string
|
||||||
|
// SkipValidation bypasses validation on Options. It should only be used
|
||||||
|
// internally for clients that need more control over their transport.
|
||||||
|
SkipValidation bool
|
||||||
|
// SkipUniverseDomainValidation skips the verification that the universe
|
||||||
|
// domain configured for the client matches the universe domain configured
|
||||||
|
// for the credentials. It should only be used internally for clients that
|
||||||
|
// need more control over their transport. The default is false.
|
||||||
|
SkipUniverseDomainValidation bool
|
||||||
|
// TelemetryAttributes specifies a map of telemetry attributes to be added
|
||||||
|
// to all OpenTelemetry signals, such as tracing and metrics, for purposes
|
||||||
|
// including representing the static identity of the client (e.g., service
|
||||||
|
// name, version). These attributes are expected to be consistent across all
|
||||||
|
// signals to enable cross-signal correlation.
|
||||||
|
//
|
||||||
|
// It should only be used internally by generated clients. Callers should not
|
||||||
|
// modify the map after it is passed in.
|
||||||
|
TelemetryAttributes map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAuthorizationMiddleware adds a middleware to the provided client's
|
||||||
|
// transport that sets the Authorization header with the value produced by the
|
||||||
|
// provided [cloud.google.com/go/auth.Credentials]. An error is returned only
|
||||||
|
// if client or creds is nil.
|
||||||
|
//
|
||||||
|
// This function does not support setting a universe domain value on the client.
|
||||||
|
func AddAuthorizationMiddleware(client *http.Client, creds *auth.Credentials) error {
|
||||||
|
if client == nil || creds == nil {
|
||||||
|
return fmt.Errorf("httptransport: client and tp must not be nil")
|
||||||
|
}
|
||||||
|
base := client.Transport
|
||||||
|
if base == nil {
|
||||||
|
if dt, ok := http.DefaultTransport.(*http.Transport); ok {
|
||||||
|
base = dt.Clone()
|
||||||
|
} else {
|
||||||
|
// Directly reuse the DefaultTransport if the application has
|
||||||
|
// replaced it with an implementation of RoundTripper other than
|
||||||
|
// http.Transport.
|
||||||
|
base = http.DefaultTransport
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client.Transport = &authTransport{
|
||||||
|
creds: creds,
|
||||||
|
base: base,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a [net/http.Client] that can be used to communicate with a
|
||||||
|
// Google cloud service, configured with the provided [Options]. It
|
||||||
|
// automatically appends Authorization headers to all outgoing requests.
|
||||||
|
func NewClient(opts *Options) (*http.Client, error) {
|
||||||
|
if err := opts.validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tOpts := &transport.Options{
|
||||||
|
Endpoint: opts.Endpoint,
|
||||||
|
ClientCertProvider: opts.ClientCertProvider,
|
||||||
|
Client: opts.client(),
|
||||||
|
UniverseDomain: opts.UniverseDomain,
|
||||||
|
Logger: opts.logger(),
|
||||||
|
}
|
||||||
|
if io := opts.InternalOptions; io != nil {
|
||||||
|
tOpts.DefaultEndpointTemplate = io.DefaultEndpointTemplate
|
||||||
|
tOpts.DefaultMTLSEndpoint = io.DefaultMTLSEndpoint
|
||||||
|
}
|
||||||
|
clientCertProvider, dialTLSContext, err := transport.GetHTTPTransportConfig(tOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
baseRoundTripper := opts.BaseRoundTripper
|
||||||
|
if baseRoundTripper == nil {
|
||||||
|
baseRoundTripper = defaultBaseTransport(clientCertProvider, dialTLSContext)
|
||||||
|
}
|
||||||
|
// Ensure the token exchange transport uses the same ClientCertProvider as the API transport.
|
||||||
|
opts.ClientCertProvider = clientCertProvider
|
||||||
|
trans, err := newTransport(baseRoundTripper, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &http.Client{
|
||||||
|
Transport: trans,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAuthHeader uses the provided token to set the Authorization and trust
|
||||||
|
// boundary headers on an http.Request. If the token.Type is empty, the type is
|
||||||
|
// assumed to be Bearer. This is the recommended way to set authorization
|
||||||
|
// headers on a custom http.Request.
|
||||||
|
func SetAuthHeader(token *auth.Token, req *http.Request) {
|
||||||
|
headers.SetAuthHeader(token, req)
|
||||||
|
}
|
||||||
235
vendor/cloud.google.com/go/auth/httptransport/transport.go
generated
vendored
Normal file
235
vendor/cloud.google.com/go/auth/httptransport/transport.go
generated
vendored
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package httptransport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"cloud.google.com/go/auth/credentials"
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"cloud.google.com/go/auth/internal/transport"
|
||||||
|
"cloud.google.com/go/auth/internal/transport/cert"
|
||||||
|
"cloud.google.com/go/auth/internal/transport/headers"
|
||||||
|
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
quotaProjectHeaderKey = "X-goog-user-project"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTransport(base http.RoundTripper, opts *Options) (http.RoundTripper, error) {
|
||||||
|
var headers = opts.Headers
|
||||||
|
ht := &headerTransport{
|
||||||
|
base: base,
|
||||||
|
headers: headers,
|
||||||
|
}
|
||||||
|
var trans http.RoundTripper = ht
|
||||||
|
trans = addOpenTelemetryTransport(trans, opts)
|
||||||
|
switch {
|
||||||
|
case opts.DisableAuthentication:
|
||||||
|
// Do nothing.
|
||||||
|
case opts.APIKey != "":
|
||||||
|
qp := internal.GetQuotaProject(nil, opts.Headers.Get(quotaProjectHeaderKey))
|
||||||
|
if qp != "" {
|
||||||
|
if headers == nil {
|
||||||
|
headers = make(map[string][]string, 1)
|
||||||
|
}
|
||||||
|
headers.Set(quotaProjectHeaderKey, qp)
|
||||||
|
}
|
||||||
|
trans = &apiKeyTransport{
|
||||||
|
Transport: trans,
|
||||||
|
Key: opts.APIKey,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
var creds *auth.Credentials
|
||||||
|
if opts.Credentials != nil {
|
||||||
|
creds = opts.Credentials
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
creds, err = credentials.DetectDefault(opts.resolveDetectOptions())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qp, err := creds.QuotaProjectID(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if qp != "" {
|
||||||
|
if headers == nil {
|
||||||
|
headers = make(map[string][]string, 1)
|
||||||
|
}
|
||||||
|
// Don't overwrite user specified quota
|
||||||
|
if v := headers.Get(quotaProjectHeaderKey); v == "" {
|
||||||
|
headers.Set(quotaProjectHeaderKey, qp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var skipUD bool
|
||||||
|
if iOpts := opts.InternalOptions; iOpts != nil {
|
||||||
|
skipUD = iOpts.SkipUniverseDomainValidation
|
||||||
|
}
|
||||||
|
creds.TokenProvider = auth.NewCachedTokenProvider(creds.TokenProvider, nil)
|
||||||
|
trans = &authTransport{
|
||||||
|
base: trans,
|
||||||
|
creds: creds,
|
||||||
|
clientUniverseDomain: opts.UniverseDomain,
|
||||||
|
skipUniverseDomainValidation: skipUD,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return trans, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultBaseTransport returns the base HTTP transport.
|
||||||
|
// On App Engine, this is urlfetch.Transport.
|
||||||
|
// Otherwise, use a default transport, taking most defaults from
|
||||||
|
// http.DefaultTransport.
|
||||||
|
// If TLSCertificate is available, set TLSClientConfig as well.
|
||||||
|
func defaultBaseTransport(clientCertSource cert.Provider, dialTLSContext func(context.Context, string, string) (net.Conn, error)) http.RoundTripper {
|
||||||
|
defaultTransport, ok := http.DefaultTransport.(*http.Transport)
|
||||||
|
if !ok {
|
||||||
|
defaultTransport = transport.BaseTransport()
|
||||||
|
}
|
||||||
|
trans := defaultTransport.Clone()
|
||||||
|
trans.MaxIdleConnsPerHost = 100
|
||||||
|
|
||||||
|
if clientCertSource != nil {
|
||||||
|
trans.TLSClientConfig = &tls.Config{
|
||||||
|
GetClientCertificate: clientCertSource,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dialTLSContext != nil {
|
||||||
|
// If DialTLSContext is set, TLSClientConfig wil be ignored
|
||||||
|
trans.DialTLSContext = dialTLSContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configures the ReadIdleTimeout HTTP/2 option for the
|
||||||
|
// transport. This allows broken idle connections to be pruned more quickly,
|
||||||
|
// preventing the client from attempting to re-use connections that will no
|
||||||
|
// longer work.
|
||||||
|
http2Trans, err := http2.ConfigureTransports(trans)
|
||||||
|
if err == nil {
|
||||||
|
http2Trans.ReadIdleTimeout = time.Second * 31
|
||||||
|
}
|
||||||
|
|
||||||
|
return trans
|
||||||
|
}
|
||||||
|
|
||||||
|
type apiKeyTransport struct {
|
||||||
|
// Key is the API Key to set on requests.
|
||||||
|
Key string
|
||||||
|
// Transport is the underlying HTTP transport.
|
||||||
|
// If nil, http.DefaultTransport is used.
|
||||||
|
Transport http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *apiKeyTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
newReq := *req
|
||||||
|
args := newReq.URL.Query()
|
||||||
|
args.Set("key", t.Key)
|
||||||
|
newReq.URL.RawQuery = args.Encode()
|
||||||
|
return t.Transport.RoundTrip(&newReq)
|
||||||
|
}
|
||||||
|
|
||||||
|
type headerTransport struct {
|
||||||
|
headers http.Header
|
||||||
|
base http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *headerTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
rt := t.base
|
||||||
|
newReq := *req
|
||||||
|
newReq.Header = make(http.Header)
|
||||||
|
for k, vv := range req.Header {
|
||||||
|
newReq.Header[k] = vv
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range t.headers {
|
||||||
|
newReq.Header[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return rt.RoundTrip(&newReq)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addOpenTelemetryTransport(trans http.RoundTripper, opts *Options) http.RoundTripper {
|
||||||
|
if opts.DisableTelemetry {
|
||||||
|
return trans
|
||||||
|
}
|
||||||
|
return otelhttp.NewTransport(trans)
|
||||||
|
}
|
||||||
|
|
||||||
|
type authTransport struct {
|
||||||
|
creds *auth.Credentials
|
||||||
|
base http.RoundTripper
|
||||||
|
clientUniverseDomain string
|
||||||
|
skipUniverseDomainValidation bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// getClientUniverseDomain returns the default service domain for a given Cloud
|
||||||
|
// universe, with the following precedence:
|
||||||
|
//
|
||||||
|
// 1. A non-empty option.WithUniverseDomain or similar client option.
|
||||||
|
// 2. A non-empty environment variable GOOGLE_CLOUD_UNIVERSE_DOMAIN.
|
||||||
|
// 3. The default value "googleapis.com".
|
||||||
|
//
|
||||||
|
// This is the universe domain configured for the client, which will be compared
|
||||||
|
// to the universe domain that is separately configured for the credentials.
|
||||||
|
func (t *authTransport) getClientUniverseDomain() string {
|
||||||
|
if t.clientUniverseDomain != "" {
|
||||||
|
return t.clientUniverseDomain
|
||||||
|
}
|
||||||
|
if envUD := os.Getenv(internal.UniverseDomainEnvVar); envUD != "" {
|
||||||
|
return envUD
|
||||||
|
}
|
||||||
|
return internal.DefaultUniverseDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundTrip authorizes and authenticates the request with an
|
||||||
|
// access token from Transport's Source. Per the RoundTripper contract we must
|
||||||
|
// not modify the initial request, so we clone it, and we must close the body
|
||||||
|
// on any errors that happens during our token logic.
|
||||||
|
func (t *authTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
reqBodyClosed := false
|
||||||
|
if req.Body != nil {
|
||||||
|
defer func() {
|
||||||
|
if !reqBodyClosed {
|
||||||
|
req.Body.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
token, err := t.creds.Token(req.Context())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !t.skipUniverseDomainValidation && token.MetadataString("auth.google.tokenSource") != "compute-metadata" {
|
||||||
|
credentialsUniverseDomain, err := t.creds.UniverseDomain(req.Context())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := transport.ValidateUniverseDomain(t.getClientUniverseDomain(), credentialsUniverseDomain); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req2 := req.Clone(req.Context())
|
||||||
|
headers.SetAuthHeader(token, req2)
|
||||||
|
reqBodyClosed = true
|
||||||
|
return t.base.RoundTrip(req2)
|
||||||
|
}
|
||||||
63
vendor/cloud.google.com/go/auth/internal/credsfile/credsfile.go
generated
vendored
Normal file
63
vendor/cloud.google.com/go/auth/internal/credsfile/credsfile.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package credsfile is meant to hide implementation details from the pubic
|
||||||
|
// surface of the detect package. It should not import any other packages in
|
||||||
|
// this module. It is located under the main internal package so other
|
||||||
|
// sub-packages can use these parsed types as well.
|
||||||
|
package credsfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// GoogleAppCredsEnvVar is the environment variable for setting the
|
||||||
|
// application default credentials.
|
||||||
|
GoogleAppCredsEnvVar = "GOOGLE_APPLICATION_CREDENTIALS"
|
||||||
|
userCredsFilename = "application_default_credentials.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetFileNameFromEnv returns the override if provided or detects a filename
|
||||||
|
// from the environment.
|
||||||
|
func GetFileNameFromEnv(override string) string {
|
||||||
|
if override != "" {
|
||||||
|
return override
|
||||||
|
}
|
||||||
|
return os.Getenv(GoogleAppCredsEnvVar)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWellKnownFileName tries to locate the filepath for the user credential
|
||||||
|
// file based on the environment.
|
||||||
|
func GetWellKnownFileName() string {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return filepath.Join(os.Getenv("APPDATA"), "gcloud", userCredsFilename)
|
||||||
|
}
|
||||||
|
return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", userCredsFilename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// guessUnixHomeDir default to checking for HOME, but not all unix systems have
|
||||||
|
// this set, do have a fallback.
|
||||||
|
func guessUnixHomeDir() string {
|
||||||
|
if v := os.Getenv("HOME"); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
if u, err := user.Current(); err == nil {
|
||||||
|
return u.HomeDir
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
159
vendor/cloud.google.com/go/auth/internal/credsfile/filetype.go
generated
vendored
Normal file
159
vendor/cloud.google.com/go/auth/internal/credsfile/filetype.go
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package credsfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config3LO is the internals of a client creds file.
|
||||||
|
type Config3LO struct {
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
ClientSecret string `json:"client_secret"`
|
||||||
|
RedirectURIs []string `json:"redirect_uris"`
|
||||||
|
AuthURI string `json:"auth_uri"`
|
||||||
|
TokenURI string `json:"token_uri"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientCredentialsFile representation.
|
||||||
|
type ClientCredentialsFile struct {
|
||||||
|
Web *Config3LO `json:"web"`
|
||||||
|
Installed *Config3LO `json:"installed"`
|
||||||
|
UniverseDomain string `json:"universe_domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceAccountFile representation.
|
||||||
|
type ServiceAccountFile struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
ProjectID string `json:"project_id"`
|
||||||
|
PrivateKeyID string `json:"private_key_id"`
|
||||||
|
PrivateKey string `json:"private_key"`
|
||||||
|
ClientEmail string `json:"client_email"`
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
AuthURL string `json:"auth_uri"`
|
||||||
|
TokenURL string `json:"token_uri"`
|
||||||
|
UniverseDomain string `json:"universe_domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserCredentialsFile representation.
|
||||||
|
type UserCredentialsFile struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
ClientSecret string `json:"client_secret"`
|
||||||
|
QuotaProjectID string `json:"quota_project_id"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
UniverseDomain string `json:"universe_domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalAccountFile representation.
|
||||||
|
type ExternalAccountFile struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
ClientSecret string `json:"client_secret"`
|
||||||
|
Audience string `json:"audience"`
|
||||||
|
SubjectTokenType string `json:"subject_token_type"`
|
||||||
|
ServiceAccountImpersonationURL string `json:"service_account_impersonation_url"`
|
||||||
|
TokenURL string `json:"token_url"`
|
||||||
|
CredentialSource *CredentialSource `json:"credential_source,omitempty"`
|
||||||
|
TokenInfoURL string `json:"token_info_url"`
|
||||||
|
ServiceAccountImpersonation *ServiceAccountImpersonationInfo `json:"service_account_impersonation,omitempty"`
|
||||||
|
QuotaProjectID string `json:"quota_project_id"`
|
||||||
|
WorkforcePoolUserProject string `json:"workforce_pool_user_project"`
|
||||||
|
UniverseDomain string `json:"universe_domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalAccountAuthorizedUserFile representation.
|
||||||
|
type ExternalAccountAuthorizedUserFile struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Audience string `json:"audience"`
|
||||||
|
ClientID string `json:"client_id"`
|
||||||
|
ClientSecret string `json:"client_secret"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
TokenURL string `json:"token_url"`
|
||||||
|
TokenInfoURL string `json:"token_info_url"`
|
||||||
|
RevokeURL string `json:"revoke_url"`
|
||||||
|
QuotaProjectID string `json:"quota_project_id"`
|
||||||
|
UniverseDomain string `json:"universe_domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CredentialSource stores the information necessary to retrieve the credentials for the STS exchange.
|
||||||
|
//
|
||||||
|
// One field amongst File, URL, Certificate, and Executable should be filled, depending on the kind of credential in question.
|
||||||
|
// The EnvironmentID should start with AWS if being used for an AWS credential.
|
||||||
|
type CredentialSource struct {
|
||||||
|
File string `json:"file"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Headers map[string]string `json:"headers"`
|
||||||
|
Executable *ExecutableConfig `json:"executable,omitempty"`
|
||||||
|
Certificate *CertificateConfig `json:"certificate"`
|
||||||
|
EnvironmentID string `json:"environment_id"` // TODO: Make type for this
|
||||||
|
RegionURL string `json:"region_url"`
|
||||||
|
RegionalCredVerificationURL string `json:"regional_cred_verification_url"`
|
||||||
|
CredVerificationURL string `json:"cred_verification_url"`
|
||||||
|
IMDSv2SessionTokenURL string `json:"imdsv2_session_token_url"`
|
||||||
|
Format *Format `json:"format,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format describes the format of a [CredentialSource].
|
||||||
|
type Format struct {
|
||||||
|
// Type is either "text" or "json". When not provided "text" type is assumed.
|
||||||
|
Type string `json:"type"`
|
||||||
|
// SubjectTokenFieldName is only required for JSON format. This would be "access_token" for azure.
|
||||||
|
SubjectTokenFieldName string `json:"subject_token_field_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecutableConfig represents the command to run for an executable
|
||||||
|
// [CredentialSource].
|
||||||
|
type ExecutableConfig struct {
|
||||||
|
Command string `json:"command"`
|
||||||
|
TimeoutMillis int `json:"timeout_millis"`
|
||||||
|
OutputFile string `json:"output_file"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateConfig represents the options used to set up X509 based workload
|
||||||
|
// [CredentialSource]
|
||||||
|
type CertificateConfig struct {
|
||||||
|
UseDefaultCertificateConfig bool `json:"use_default_certificate_config"`
|
||||||
|
CertificateConfigLocation string `json:"certificate_config_location"`
|
||||||
|
TrustChainPath string `json:"trust_chain_path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceAccountImpersonationInfo has impersonation configuration.
|
||||||
|
type ServiceAccountImpersonationInfo struct {
|
||||||
|
TokenLifetimeSeconds int `json:"token_lifetime_seconds"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImpersonatedServiceAccountFile representation.
|
||||||
|
type ImpersonatedServiceAccountFile struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
ServiceAccountImpersonationURL string `json:"service_account_impersonation_url"`
|
||||||
|
Delegates []string `json:"delegates"`
|
||||||
|
Scopes []string `json:"scopes"`
|
||||||
|
CredSource json.RawMessage `json:"source_credentials"`
|
||||||
|
UniverseDomain string `json:"universe_domain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GDCHServiceAccountFile represents the Google Distributed Cloud Hosted (GDCH) service identity file.
|
||||||
|
type GDCHServiceAccountFile struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
FormatVersion string `json:"format_version"`
|
||||||
|
Project string `json:"project"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
CertPath string `json:"ca_cert_path"`
|
||||||
|
PrivateKeyID string `json:"private_key_id"`
|
||||||
|
PrivateKey string `json:"private_key"`
|
||||||
|
TokenURL string `json:"token_uri"`
|
||||||
|
UniverseDomain string `json:"universe_domain"`
|
||||||
|
}
|
||||||
99
vendor/cloud.google.com/go/auth/internal/credsfile/parse.go
generated
vendored
Normal file
99
vendor/cloud.google.com/go/auth/internal/credsfile/parse.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package credsfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseServiceAccount parses bytes into a [ServiceAccountFile].
|
||||||
|
func ParseServiceAccount(b []byte) (*ServiceAccountFile, error) {
|
||||||
|
var f *ServiceAccountFile
|
||||||
|
if err := json.Unmarshal(b, &f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseClientCredentials parses bytes into a
|
||||||
|
// [credsfile.ClientCredentialsFile].
|
||||||
|
func ParseClientCredentials(b []byte) (*ClientCredentialsFile, error) {
|
||||||
|
var f *ClientCredentialsFile
|
||||||
|
if err := json.Unmarshal(b, &f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseUserCredentials parses bytes into a [UserCredentialsFile].
|
||||||
|
func ParseUserCredentials(b []byte) (*UserCredentialsFile, error) {
|
||||||
|
var f *UserCredentialsFile
|
||||||
|
if err := json.Unmarshal(b, &f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseExternalAccount parses bytes into a [ExternalAccountFile].
|
||||||
|
func ParseExternalAccount(b []byte) (*ExternalAccountFile, error) {
|
||||||
|
var f *ExternalAccountFile
|
||||||
|
if err := json.Unmarshal(b, &f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseExternalAccountAuthorizedUser parses bytes into a
|
||||||
|
// [ExternalAccountAuthorizedUserFile].
|
||||||
|
func ParseExternalAccountAuthorizedUser(b []byte) (*ExternalAccountAuthorizedUserFile, error) {
|
||||||
|
var f *ExternalAccountAuthorizedUserFile
|
||||||
|
if err := json.Unmarshal(b, &f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseImpersonatedServiceAccount parses bytes into a
|
||||||
|
// [ImpersonatedServiceAccountFile].
|
||||||
|
func ParseImpersonatedServiceAccount(b []byte) (*ImpersonatedServiceAccountFile, error) {
|
||||||
|
var f *ImpersonatedServiceAccountFile
|
||||||
|
if err := json.Unmarshal(b, &f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseGDCHServiceAccount parses bytes into a [GDCHServiceAccountFile].
|
||||||
|
func ParseGDCHServiceAccount(b []byte) (*GDCHServiceAccountFile, error) {
|
||||||
|
var f *GDCHServiceAccountFile
|
||||||
|
if err := json.Unmarshal(b, &f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type fileTypeChecker struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFileType determines the [CredentialType] based on bytes provided.
|
||||||
|
// Only returns error for json.Unmarshal.
|
||||||
|
func ParseFileType(b []byte) (string, error) {
|
||||||
|
var f fileTypeChecker
|
||||||
|
if err := json.Unmarshal(b, &f); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return f.Type, nil
|
||||||
|
}
|
||||||
285
vendor/cloud.google.com/go/auth/internal/internal.go
generated
vendored
Normal file
285
vendor/cloud.google.com/go/auth/internal/internal.go
generated
vendored
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/compute/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TokenTypeBearer is the auth header prefix for bearer tokens.
|
||||||
|
TokenTypeBearer = "Bearer"
|
||||||
|
|
||||||
|
// QuotaProjectEnvVar is the environment variable for setting the quota
|
||||||
|
// project.
|
||||||
|
QuotaProjectEnvVar = "GOOGLE_CLOUD_QUOTA_PROJECT"
|
||||||
|
// UniverseDomainEnvVar is the environment variable for setting the default
|
||||||
|
// service domain for a given Cloud universe.
|
||||||
|
UniverseDomainEnvVar = "GOOGLE_CLOUD_UNIVERSE_DOMAIN"
|
||||||
|
projectEnvVar = "GOOGLE_CLOUD_PROJECT"
|
||||||
|
maxBodySize = 1 << 20
|
||||||
|
|
||||||
|
// DefaultUniverseDomain is the default value for universe domain.
|
||||||
|
// Universe domain is the default service domain for a given Cloud universe.
|
||||||
|
DefaultUniverseDomain = "googleapis.com"
|
||||||
|
|
||||||
|
// TrustBoundaryNoOp is a constant indicating no trust boundary is enforced.
|
||||||
|
TrustBoundaryNoOp = "0x0"
|
||||||
|
|
||||||
|
// TrustBoundaryDataKey is the key used to store trust boundary data in a token's metadata.
|
||||||
|
TrustBoundaryDataKey = "google.auth.trust_boundary_data"
|
||||||
|
)
|
||||||
|
|
||||||
|
type clonableTransport interface {
|
||||||
|
Clone() *http.Transport
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultClient returns an [http.Client] with some defaults set. If
|
||||||
|
// the current [http.DefaultTransport] is a [clonableTransport], as
|
||||||
|
// is the case for an [*http.Transport], the clone will be used.
|
||||||
|
// Otherwise the [http.DefaultTransport] is used directly.
|
||||||
|
func DefaultClient() *http.Client {
|
||||||
|
if transport, ok := http.DefaultTransport.(clonableTransport); ok {
|
||||||
|
return &http.Client{
|
||||||
|
Transport: transport.Clone(),
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &http.Client{
|
||||||
|
Transport: http.DefaultTransport,
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseKey converts the binary contents of a private key file
|
||||||
|
// to an crypto.Signer. It detects whether the private key is in a
|
||||||
|
// PEM container or not. If so, it extracts the the private key
|
||||||
|
// from PEM container before conversion. It only supports PEM
|
||||||
|
// containers with no passphrase.
|
||||||
|
func ParseKey(key []byte) (crypto.Signer, error) {
|
||||||
|
block, _ := pem.Decode(key)
|
||||||
|
if block != nil {
|
||||||
|
key = block.Bytes
|
||||||
|
}
|
||||||
|
var parsedKey crypto.PrivateKey
|
||||||
|
|
||||||
|
var errPKCS8, errPKCS1, errEC error
|
||||||
|
if parsedKey, errPKCS8 = x509.ParsePKCS8PrivateKey(key); errPKCS8 != nil {
|
||||||
|
if parsedKey, errPKCS1 = x509.ParsePKCS1PrivateKey(key); errPKCS1 != nil {
|
||||||
|
if parsedKey, errEC = x509.ParseECPrivateKey(key); errEC != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse private key. Tried PKCS8, PKCS1, and EC formats. Errors: [PKCS8: %v], [PKCS1: %v], [EC: %v]", errPKCS8, errPKCS1, errEC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parsed, ok := parsedKey.(crypto.Signer)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("private key is not a signer")
|
||||||
|
}
|
||||||
|
return parsed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQuotaProject retrieves quota project with precedence being: override,
|
||||||
|
// environment variable, creds json file.
|
||||||
|
func GetQuotaProject(b []byte, override string) string {
|
||||||
|
if override != "" {
|
||||||
|
return override
|
||||||
|
}
|
||||||
|
if env := os.Getenv(QuotaProjectEnvVar); env != "" {
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
if b == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var v struct {
|
||||||
|
QuotaProject string `json:"quota_project_id"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return v.QuotaProject
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProjectID retrieves project with precedence being: override,
|
||||||
|
// environment variable, creds json file.
|
||||||
|
func GetProjectID(b []byte, override string) string {
|
||||||
|
if override != "" {
|
||||||
|
return override
|
||||||
|
}
|
||||||
|
if env := os.Getenv(projectEnvVar); env != "" {
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
if b == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var v struct {
|
||||||
|
ProjectID string `json:"project_id"` // standard service account key
|
||||||
|
Project string `json:"project"` // gdch key
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if v.ProjectID != "" {
|
||||||
|
return v.ProjectID
|
||||||
|
}
|
||||||
|
return v.Project
|
||||||
|
}
|
||||||
|
|
||||||
|
// DoRequest executes the provided req with the client. It reads the response
|
||||||
|
// body, closes it, and returns it.
|
||||||
|
func DoRequest(client *http.Client, req *http.Request) (*http.Response, []byte, error) {
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ReadAll(io.LimitReader(resp.Body, maxBodySize))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return resp, body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadAll consumes the whole reader and safely reads the content of its body
|
||||||
|
// with some overflow protection.
|
||||||
|
func ReadAll(r io.Reader) ([]byte, error) {
|
||||||
|
return io.ReadAll(io.LimitReader(r, maxBodySize))
|
||||||
|
}
|
||||||
|
|
||||||
|
// StaticCredentialsProperty is a helper for creating static credentials
|
||||||
|
// properties.
|
||||||
|
func StaticCredentialsProperty(s string) StaticProperty {
|
||||||
|
return StaticProperty(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StaticProperty always returns that value of the underlying string.
|
||||||
|
type StaticProperty string
|
||||||
|
|
||||||
|
// GetProperty loads the properly value provided the given context.
|
||||||
|
func (p StaticProperty) GetProperty(context.Context) (string, error) {
|
||||||
|
return string(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ComputeUniverseDomainProvider fetches the credentials universe domain from
|
||||||
|
// the google cloud metadata service.
|
||||||
|
type ComputeUniverseDomainProvider struct {
|
||||||
|
MetadataClient *metadata.Client
|
||||||
|
universeDomainOnce sync.Once
|
||||||
|
universeDomain string
|
||||||
|
universeDomainErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProperty fetches the credentials universe domain from the google cloud
|
||||||
|
// metadata service.
|
||||||
|
func (c *ComputeUniverseDomainProvider) GetProperty(ctx context.Context) (string, error) {
|
||||||
|
c.universeDomainOnce.Do(func() {
|
||||||
|
c.universeDomain, c.universeDomainErr = getMetadataUniverseDomain(ctx, c.MetadataClient)
|
||||||
|
})
|
||||||
|
if c.universeDomainErr != nil {
|
||||||
|
return "", c.universeDomainErr
|
||||||
|
}
|
||||||
|
return c.universeDomain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpGetMetadataUniverseDomain is a package var for unit test substitution.
|
||||||
|
var httpGetMetadataUniverseDomain = func(ctx context.Context, client *metadata.Client) (string, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
return client.GetWithContext(ctx, "universe/universe-domain")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMetadataUniverseDomain(ctx context.Context, client *metadata.Client) (string, error) {
|
||||||
|
universeDomain, err := httpGetMetadataUniverseDomain(ctx, client)
|
||||||
|
if err == nil {
|
||||||
|
return universeDomain, nil
|
||||||
|
}
|
||||||
|
if _, ok := err.(metadata.NotDefinedError); ok {
|
||||||
|
// http.StatusNotFound (404)
|
||||||
|
return DefaultUniverseDomain, nil
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatIAMServiceAccountResource sets a service account name in an IAM resource
|
||||||
|
// name.
|
||||||
|
func FormatIAMServiceAccountResource(name string) string {
|
||||||
|
return fmt.Sprintf("projects/-/serviceAccounts/%s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrustBoundaryData represents the trust boundary data associated with a token.
|
||||||
|
// It contains information about the regions or environments where the token is valid.
|
||||||
|
type TrustBoundaryData struct {
|
||||||
|
// Locations is the list of locations that the token is allowed to be used in.
|
||||||
|
Locations []string
|
||||||
|
// EncodedLocations represents the locations in an encoded format.
|
||||||
|
EncodedLocations string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTrustBoundaryData returns a new TrustBoundaryData with the specified locations and encoded locations.
|
||||||
|
func NewTrustBoundaryData(locations []string, encodedLocations string) *TrustBoundaryData {
|
||||||
|
// Ensure consistency by treating a nil slice as an empty slice.
|
||||||
|
if locations == nil {
|
||||||
|
locations = []string{}
|
||||||
|
}
|
||||||
|
locationsCopy := make([]string, len(locations))
|
||||||
|
copy(locationsCopy, locations)
|
||||||
|
return &TrustBoundaryData{
|
||||||
|
Locations: locationsCopy,
|
||||||
|
EncodedLocations: encodedLocations,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNoOpTrustBoundaryData returns a new TrustBoundaryData with no restrictions.
|
||||||
|
func NewNoOpTrustBoundaryData() *TrustBoundaryData {
|
||||||
|
return &TrustBoundaryData{
|
||||||
|
Locations: []string{},
|
||||||
|
EncodedLocations: TrustBoundaryNoOp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrustBoundaryHeader returns the value for the x-allowed-locations header and a bool
|
||||||
|
// indicating if the header should be set. The return values are structured to
|
||||||
|
// handle three distinct states required by the backend:
|
||||||
|
// 1. Header not set: (value="", present=false) -> data is empty.
|
||||||
|
// 2. Header set to an empty string: (value="", present=true) -> data is a no-op.
|
||||||
|
// 3. Header set to a value: (value="...", present=true) -> data has locations.
|
||||||
|
func (t TrustBoundaryData) TrustBoundaryHeader() (value string, present bool) {
|
||||||
|
if t.EncodedLocations == "" {
|
||||||
|
// If the data is empty, the header should not be present.
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If data is not empty, the header should always be present.
|
||||||
|
present = true
|
||||||
|
value = ""
|
||||||
|
if t.EncodedLocations != TrustBoundaryNoOp {
|
||||||
|
value = t.EncodedLocations
|
||||||
|
}
|
||||||
|
// For a no-op, the backend requires an empty string.
|
||||||
|
return value, present
|
||||||
|
}
|
||||||
171
vendor/cloud.google.com/go/auth/internal/jwt/jwt.go
generated
vendored
Normal file
171
vendor/cloud.google.com/go/auth/internal/jwt/jwt.go
generated
vendored
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package jwt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// HeaderAlgRSA256 is the RS256 [Header.Algorithm].
|
||||||
|
HeaderAlgRSA256 = "RS256"
|
||||||
|
// HeaderAlgES256 is the ES256 [Header.Algorithm].
|
||||||
|
HeaderAlgES256 = "ES256"
|
||||||
|
// HeaderType is the standard [Header.Type].
|
||||||
|
HeaderType = "JWT"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Header represents a JWT header.
|
||||||
|
type Header struct {
|
||||||
|
Algorithm string `json:"alg"`
|
||||||
|
Type string `json:"typ"`
|
||||||
|
KeyID string `json:"kid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Header) encode() (string, error) {
|
||||||
|
b, err := json.Marshal(h)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Claims represents the claims set of a JWT.
|
||||||
|
type Claims struct {
|
||||||
|
// Iss is the issuer JWT claim.
|
||||||
|
Iss string `json:"iss"`
|
||||||
|
// Scope is the scope JWT claim.
|
||||||
|
Scope string `json:"scope,omitempty"`
|
||||||
|
// Exp is the expiry JWT claim. If unset, default is in one hour from now.
|
||||||
|
Exp int64 `json:"exp"`
|
||||||
|
// Iat is the subject issued at claim. If unset, default is now.
|
||||||
|
Iat int64 `json:"iat"`
|
||||||
|
// Aud is the audience JWT claim. Optional.
|
||||||
|
Aud string `json:"aud"`
|
||||||
|
// Sub is the subject JWT claim. Optional.
|
||||||
|
Sub string `json:"sub,omitempty"`
|
||||||
|
// AdditionalClaims contains any additional non-standard JWT claims. Optional.
|
||||||
|
AdditionalClaims map[string]interface{} `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Claims) encode() (string, error) {
|
||||||
|
// Compensate for skew
|
||||||
|
now := time.Now().Add(-10 * time.Second)
|
||||||
|
if c.Iat == 0 {
|
||||||
|
c.Iat = now.Unix()
|
||||||
|
}
|
||||||
|
if c.Exp == 0 {
|
||||||
|
c.Exp = now.Add(time.Hour).Unix()
|
||||||
|
}
|
||||||
|
if c.Exp < c.Iat {
|
||||||
|
return "", fmt.Errorf("jwt: invalid Exp = %d; must be later than Iat = %d", c.Exp, c.Iat)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.AdditionalClaims) == 0 {
|
||||||
|
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal private claim set and then append it to b.
|
||||||
|
prv, err := json.Marshal(c.AdditionalClaims)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid map of additional claims %v: %w", c.AdditionalClaims, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concatenate public and private claim JSON objects.
|
||||||
|
if !bytes.HasSuffix(b, []byte{'}'}) {
|
||||||
|
return "", fmt.Errorf("invalid JSON %s", b)
|
||||||
|
}
|
||||||
|
if !bytes.HasPrefix(prv, []byte{'{'}) {
|
||||||
|
return "", 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 base64.RawURLEncoding.EncodeToString(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeJWS encodes the data using the provided key as a JSON web signature.
|
||||||
|
func EncodeJWS(header *Header, c *Claims, signer crypto.Signer) (string, error) {
|
||||||
|
head, err := header.encode()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
claims, err := c.encode()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ss := fmt.Sprintf("%s.%s", head, claims)
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write([]byte(ss))
|
||||||
|
sig, err := signer.Sign(rand.Reader, h.Sum(nil), crypto.SHA256)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s.%s", ss, base64.RawURLEncoding.EncodeToString(sig)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeJWS decodes a claim set from a JWS payload.
|
||||||
|
func DecodeJWS(payload string) (*Claims, error) {
|
||||||
|
// decode returned id token to get expiry
|
||||||
|
s := strings.Split(payload, ".")
|
||||||
|
if len(s) < 2 {
|
||||||
|
return nil, errors.New("invalid token received")
|
||||||
|
}
|
||||||
|
decoded, err := base64.RawURLEncoding.DecodeString(s[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c := &Claims{}
|
||||||
|
if err := json.NewDecoder(bytes.NewBuffer(decoded)).Decode(c); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(bytes.NewBuffer(decoded)).Decode(&c.AdditionalClaims); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyJWS tests whether the provided JWT token's signature was produced by
|
||||||
|
// the private key associated with the provided public key.
|
||||||
|
func VerifyJWS(token string, key *rsa.PublicKey) error {
|
||||||
|
parts := strings.Split(token, ".")
|
||||||
|
if len(parts) != 3 {
|
||||||
|
return errors.New("jwt: invalid token received, token must have 3 parts")
|
||||||
|
}
|
||||||
|
|
||||||
|
signedContent := parts[0] + "." + parts[1]
|
||||||
|
signatureString, err := base64.RawURLEncoding.DecodeString(parts[2])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write([]byte(signedContent))
|
||||||
|
return rsa.VerifyPKCS1v15(key, crypto.SHA256, h.Sum(nil), signatureString)
|
||||||
|
}
|
||||||
117
vendor/cloud.google.com/go/auth/internal/retry/retry.go
generated
vendored
Normal file
117
vendor/cloud.google.com/go/auth/internal/retry/retry.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
// Copyright 2025 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package retry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxRetryAttempts = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
syscallRetryable = func(error) bool { return false }
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultBackoff is basically equivalent to gax.Backoff without the need for
|
||||||
|
// the dependency.
|
||||||
|
type defaultBackoff struct {
|
||||||
|
max time.Duration
|
||||||
|
mul float64
|
||||||
|
cur time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *defaultBackoff) Pause() time.Duration {
|
||||||
|
d := time.Duration(1 + rand.Int63n(int64(b.cur)))
|
||||||
|
b.cur = time.Duration(float64(b.cur) * b.mul)
|
||||||
|
if b.cur > b.max {
|
||||||
|
b.cur = b.max
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sleep is the equivalent of gax.Sleep without the need for the dependency.
|
||||||
|
func Sleep(ctx context.Context, d time.Duration) error {
|
||||||
|
t := time.NewTimer(d)
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Stop()
|
||||||
|
return ctx.Err()
|
||||||
|
case <-t.C:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Retryer with the default backoff strategy.
|
||||||
|
func New() *Retryer {
|
||||||
|
return &Retryer{bo: &defaultBackoff{
|
||||||
|
cur: 100 * time.Millisecond,
|
||||||
|
max: 30 * time.Second,
|
||||||
|
mul: 2,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type backoff interface {
|
||||||
|
Pause() time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retryer is a retryer for HTTP requests.
|
||||||
|
type Retryer struct {
|
||||||
|
bo backoff
|
||||||
|
attempts int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry determines if a request should be retried.
|
||||||
|
func (r *Retryer) Retry(status int, err error) (time.Duration, bool) {
|
||||||
|
if status == http.StatusOK {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
retryOk := shouldRetry(status, err)
|
||||||
|
if !retryOk {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
if r.attempts == maxRetryAttempts {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
r.attempts++
|
||||||
|
return r.bo.Pause(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldRetry(status int, err error) bool {
|
||||||
|
if 500 <= status && status <= 599 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if err == io.ErrUnexpectedEOF {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Transient network errors should be retried.
|
||||||
|
if syscallRetryable(err) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if err, ok := err.(interface{ Temporary() bool }); ok {
|
||||||
|
if err.Temporary() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err, ok := err.(interface{ Unwrap() error }); ok {
|
||||||
|
return shouldRetry(status, err.Unwrap())
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
361
vendor/cloud.google.com/go/auth/internal/transport/cba.go
generated
vendored
Normal file
361
vendor/cloud.google.com/go/auth/internal/transport/cba.go
generated
vendored
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"cloud.google.com/go/auth/internal/transport/cert"
|
||||||
|
"github.com/google/s2a-go"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mTLSModeAlways = "always"
|
||||||
|
mTLSModeNever = "never"
|
||||||
|
mTLSModeAuto = "auto"
|
||||||
|
|
||||||
|
// Experimental: if true, the code will try MTLS with S2A as the default for transport security. Default value is false.
|
||||||
|
googleAPIUseS2AEnv = "EXPERIMENTAL_GOOGLE_API_USE_S2A"
|
||||||
|
googleAPIUseCertSource = "GOOGLE_API_USE_CLIENT_CERTIFICATE"
|
||||||
|
googleAPIUseMTLS = "GOOGLE_API_USE_MTLS_ENDPOINT"
|
||||||
|
googleAPIUseMTLSOld = "GOOGLE_API_USE_MTLS"
|
||||||
|
|
||||||
|
universeDomainPlaceholder = "UNIVERSE_DOMAIN"
|
||||||
|
|
||||||
|
mtlsMDSRoot = "/run/google-mds-mtls/root.crt"
|
||||||
|
mtlsMDSKey = "/run/google-mds-mtls/client.key"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type represents the type of transport used.
|
||||||
|
type Type int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TransportTypeUnknown represents an unknown transport type and is the default option.
|
||||||
|
TransportTypeUnknown Type = iota
|
||||||
|
// TransportTypeMTLSS2A represents the mTLS transport type using S2A.
|
||||||
|
TransportTypeMTLSS2A
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options is a struct that is duplicated information from the individual
|
||||||
|
// transport packages in order to avoid cyclic deps. It correlates 1:1 with
|
||||||
|
// fields on httptransport.Options and grpctransport.Options.
|
||||||
|
type Options struct {
|
||||||
|
Endpoint string
|
||||||
|
DefaultEndpointTemplate string
|
||||||
|
DefaultMTLSEndpoint string
|
||||||
|
ClientCertProvider cert.Provider
|
||||||
|
Client *http.Client
|
||||||
|
UniverseDomain string
|
||||||
|
EnableDirectPath bool
|
||||||
|
EnableDirectPathXds bool
|
||||||
|
Logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// getUniverseDomain returns the default service domain for a given Cloud
|
||||||
|
// universe.
|
||||||
|
func (o *Options) getUniverseDomain() string {
|
||||||
|
if o.UniverseDomain == "" {
|
||||||
|
return internal.DefaultUniverseDomain
|
||||||
|
}
|
||||||
|
return o.UniverseDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
// isUniverseDomainGDU returns true if the universe domain is the default Google
|
||||||
|
// universe.
|
||||||
|
func (o *Options) isUniverseDomainGDU() bool {
|
||||||
|
return o.getUniverseDomain() == internal.DefaultUniverseDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultEndpoint returns the DefaultEndpointTemplate merged with the
|
||||||
|
// universe domain if the DefaultEndpointTemplate is set, otherwise returns an
|
||||||
|
// empty string.
|
||||||
|
func (o *Options) defaultEndpoint() string {
|
||||||
|
if o.DefaultEndpointTemplate == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.Replace(o.DefaultEndpointTemplate, universeDomainPlaceholder, o.getUniverseDomain(), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultMTLSEndpoint returns the DefaultMTLSEndpointTemplate merged with the
|
||||||
|
// universe domain if the DefaultMTLSEndpointTemplate is set, otherwise returns an
|
||||||
|
// empty string.
|
||||||
|
func (o *Options) defaultMTLSEndpoint() string {
|
||||||
|
if o.DefaultMTLSEndpoint == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.Replace(o.DefaultMTLSEndpoint, universeDomainPlaceholder, o.getUniverseDomain(), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergedEndpoint merges a user-provided Endpoint of format host[:port] with the
|
||||||
|
// default endpoint.
|
||||||
|
func (o *Options) mergedEndpoint() (string, error) {
|
||||||
|
defaultEndpoint := o.defaultEndpoint()
|
||||||
|
u, err := url.Parse(fixScheme(defaultEndpoint))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return strings.Replace(defaultEndpoint, u.Host, o.Endpoint, 1), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fixScheme(baseURL string) string {
|
||||||
|
if !strings.Contains(baseURL, "://") {
|
||||||
|
baseURL = "https://" + baseURL
|
||||||
|
}
|
||||||
|
return baseURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// GRPCTransportCredentials embeds interface TransportCredentials with additional data.
|
||||||
|
type GRPCTransportCredentials struct {
|
||||||
|
credentials.TransportCredentials
|
||||||
|
Endpoint string
|
||||||
|
TransportType Type
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGRPCTransportCredsAndEndpoint returns an instance of
|
||||||
|
// [google.golang.org/grpc/credentials.TransportCredentials], and the
|
||||||
|
// corresponding endpoint and transport type to use for GRPC client.
|
||||||
|
func GetGRPCTransportCredsAndEndpoint(opts *Options) (*GRPCTransportCredentials, error) {
|
||||||
|
config, err := getTransportConfig(opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultTransportCreds := credentials.NewTLS(&tls.Config{
|
||||||
|
GetClientCertificate: config.clientCertSource,
|
||||||
|
})
|
||||||
|
|
||||||
|
var s2aAddr string
|
||||||
|
var transportCredsForS2A credentials.TransportCredentials
|
||||||
|
|
||||||
|
if config.mtlsS2AAddress != "" {
|
||||||
|
s2aAddr = config.mtlsS2AAddress
|
||||||
|
transportCredsForS2A, err = loadMTLSMDSTransportCreds(mtlsMDSRoot, mtlsMDSKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Loading MTLS MDS credentials failed: %v", err)
|
||||||
|
if config.s2aAddress != "" {
|
||||||
|
s2aAddr = config.s2aAddress
|
||||||
|
} else {
|
||||||
|
return &GRPCTransportCredentials{defaultTransportCreds, config.endpoint, TransportTypeUnknown}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if config.s2aAddress != "" {
|
||||||
|
s2aAddr = config.s2aAddress
|
||||||
|
} else {
|
||||||
|
return &GRPCTransportCredentials{defaultTransportCreds, config.endpoint, TransportTypeUnknown}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s2aTransportCreds, err := s2a.NewClientCreds(&s2a.ClientOptions{
|
||||||
|
S2AAddress: s2aAddr,
|
||||||
|
TransportCreds: transportCredsForS2A,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// Use default if we cannot initialize S2A client transport credentials.
|
||||||
|
return &GRPCTransportCredentials{defaultTransportCreds, config.endpoint, TransportTypeUnknown}, nil
|
||||||
|
}
|
||||||
|
return &GRPCTransportCredentials{s2aTransportCreds, config.s2aMTLSEndpoint, TransportTypeMTLSS2A}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHTTPTransportConfig returns a client certificate source and a function for
|
||||||
|
// dialing MTLS with S2A.
|
||||||
|
func GetHTTPTransportConfig(opts *Options) (cert.Provider, func(context.Context, string, string) (net.Conn, error), error) {
|
||||||
|
config, err := getTransportConfig(opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var s2aAddr string
|
||||||
|
var transportCredsForS2A credentials.TransportCredentials
|
||||||
|
|
||||||
|
if config.mtlsS2AAddress != "" {
|
||||||
|
s2aAddr = config.mtlsS2AAddress
|
||||||
|
transportCredsForS2A, err = loadMTLSMDSTransportCreds(mtlsMDSRoot, mtlsMDSKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Loading MTLS MDS credentials failed: %v", err)
|
||||||
|
if config.s2aAddress != "" {
|
||||||
|
s2aAddr = config.s2aAddress
|
||||||
|
} else {
|
||||||
|
return config.clientCertSource, nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if config.s2aAddress != "" {
|
||||||
|
s2aAddr = config.s2aAddress
|
||||||
|
} else {
|
||||||
|
return config.clientCertSource, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dialTLSContextFunc := s2a.NewS2ADialTLSContextFunc(&s2a.ClientOptions{
|
||||||
|
S2AAddress: s2aAddr,
|
||||||
|
TransportCreds: transportCredsForS2A,
|
||||||
|
})
|
||||||
|
return nil, dialTLSContextFunc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadMTLSMDSTransportCreds(mtlsMDSRootFile, mtlsMDSKeyFile string) (credentials.TransportCredentials, error) {
|
||||||
|
rootPEM, err := os.ReadFile(mtlsMDSRootFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
caCertPool := x509.NewCertPool()
|
||||||
|
ok := caCertPool.AppendCertsFromPEM(rootPEM)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("failed to load MTLS MDS root certificate")
|
||||||
|
}
|
||||||
|
// The mTLS MDS credentials are formatted as the concatenation of a PEM-encoded certificate chain
|
||||||
|
// followed by a PEM-encoded private key. For this reason, the concatenation is passed in to the
|
||||||
|
// tls.X509KeyPair function as both the certificate chain and private key arguments.
|
||||||
|
cert, err := tls.LoadX509KeyPair(mtlsMDSKeyFile, mtlsMDSKeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tlsConfig := tls.Config{
|
||||||
|
RootCAs: caCertPool,
|
||||||
|
Certificates: []tls.Certificate{cert},
|
||||||
|
MinVersion: tls.VersionTLS13,
|
||||||
|
}
|
||||||
|
return credentials.NewTLS(&tlsConfig), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTransportConfig(opts *Options) (*transportConfig, error) {
|
||||||
|
clientCertSource, err := GetClientCertificateProvider(opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
endpoint, err := getEndpoint(opts, clientCertSource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defaultTransportConfig := transportConfig{
|
||||||
|
clientCertSource: clientCertSource,
|
||||||
|
endpoint: endpoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !shouldUseS2A(clientCertSource, opts) {
|
||||||
|
return &defaultTransportConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s2aAddress := GetS2AAddress(opts.Logger)
|
||||||
|
mtlsS2AAddress := GetMTLSS2AAddress(opts.Logger)
|
||||||
|
if s2aAddress == "" && mtlsS2AAddress == "" {
|
||||||
|
return &defaultTransportConfig, nil
|
||||||
|
}
|
||||||
|
return &transportConfig{
|
||||||
|
clientCertSource: clientCertSource,
|
||||||
|
endpoint: endpoint,
|
||||||
|
s2aAddress: s2aAddress,
|
||||||
|
mtlsS2AAddress: mtlsS2AAddress,
|
||||||
|
s2aMTLSEndpoint: opts.defaultMTLSEndpoint(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetClientCertificateProvider returns a default client certificate source, if
|
||||||
|
// not provided by the user.
|
||||||
|
//
|
||||||
|
// A nil default source can be returned if the source does not exist. Any exceptions
|
||||||
|
// encountered while initializing the default source will be reported as client
|
||||||
|
// error (ex. corrupt metadata file).
|
||||||
|
func GetClientCertificateProvider(opts *Options) (cert.Provider, error) {
|
||||||
|
if !isClientCertificateEnabled(opts) {
|
||||||
|
return nil, nil
|
||||||
|
} else if opts.ClientCertProvider != nil {
|
||||||
|
return opts.ClientCertProvider, nil
|
||||||
|
}
|
||||||
|
return cert.DefaultProvider()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// isClientCertificateEnabled returns true by default for all GDU universe domain, unless explicitly overridden by env var
|
||||||
|
func isClientCertificateEnabled(opts *Options) bool {
|
||||||
|
if value, ok := os.LookupEnv(googleAPIUseCertSource); ok {
|
||||||
|
// error as false is OK
|
||||||
|
b, _ := strconv.ParseBool(value)
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
return opts.isUniverseDomainGDU()
|
||||||
|
}
|
||||||
|
|
||||||
|
type transportConfig struct {
|
||||||
|
// The client certificate source.
|
||||||
|
clientCertSource cert.Provider
|
||||||
|
// The corresponding endpoint to use based on client certificate source.
|
||||||
|
endpoint string
|
||||||
|
// The plaintext S2A address if it can be used, otherwise an empty string.
|
||||||
|
s2aAddress string
|
||||||
|
// The MTLS S2A address if it can be used, otherwise an empty string.
|
||||||
|
mtlsS2AAddress string
|
||||||
|
// The MTLS endpoint to use with S2A.
|
||||||
|
s2aMTLSEndpoint string
|
||||||
|
}
|
||||||
|
|
||||||
|
// getEndpoint returns the endpoint for the service, taking into account the
|
||||||
|
// user-provided endpoint override "settings.Endpoint".
|
||||||
|
//
|
||||||
|
// If no endpoint override is specified, we will either return the default
|
||||||
|
// endpoint or the default mTLS endpoint if a client certificate is available.
|
||||||
|
//
|
||||||
|
// You can override the default endpoint choice (mTLS vs. regular) by setting
|
||||||
|
// the GOOGLE_API_USE_MTLS_ENDPOINT environment variable.
|
||||||
|
//
|
||||||
|
// If the endpoint override is an address (host:port) rather than full base
|
||||||
|
// URL (ex. https://...), then the user-provided address will be merged into
|
||||||
|
// the default endpoint. For example, WithEndpoint("myhost:8000") and
|
||||||
|
// DefaultEndpointTemplate("https://UNIVERSE_DOMAIN/bar/baz") will return
|
||||||
|
// "https://myhost:8080/bar/baz". Note that this does not apply to the mTLS
|
||||||
|
// endpoint.
|
||||||
|
func getEndpoint(opts *Options, clientCertSource cert.Provider) (string, error) {
|
||||||
|
if opts.Endpoint == "" {
|
||||||
|
mtlsMode := getMTLSMode()
|
||||||
|
if mtlsMode == mTLSModeAlways || (clientCertSource != nil && mtlsMode == mTLSModeAuto) {
|
||||||
|
return opts.defaultMTLSEndpoint(), nil
|
||||||
|
}
|
||||||
|
return opts.defaultEndpoint(), nil
|
||||||
|
}
|
||||||
|
if strings.Contains(opts.Endpoint, "://") {
|
||||||
|
// User passed in a full URL path, use it verbatim.
|
||||||
|
return opts.Endpoint, nil
|
||||||
|
}
|
||||||
|
if opts.defaultEndpoint() == "" {
|
||||||
|
// If DefaultEndpointTemplate is not configured,
|
||||||
|
// use the user provided endpoint verbatim. This allows a naked
|
||||||
|
// "host[:port]" URL to be used with GRPC Direct Path.
|
||||||
|
return opts.Endpoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume user-provided endpoint is host[:port], merge it with the default endpoint.
|
||||||
|
return opts.mergedEndpoint()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMTLSMode() string {
|
||||||
|
mode := os.Getenv(googleAPIUseMTLS)
|
||||||
|
if mode == "" {
|
||||||
|
mode = os.Getenv(googleAPIUseMTLSOld) // Deprecated.
|
||||||
|
}
|
||||||
|
if mode == "" {
|
||||||
|
return mTLSModeAuto
|
||||||
|
}
|
||||||
|
return strings.ToLower(mode)
|
||||||
|
}
|
||||||
65
vendor/cloud.google.com/go/auth/internal/transport/cert/default_cert.go
generated
vendored
Normal file
65
vendor/cloud.google.com/go/auth/internal/transport/cert/default_cert.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultCertData holds all the variables pertaining to
|
||||||
|
// the default certificate provider created by [DefaultProvider].
|
||||||
|
//
|
||||||
|
// A singleton model is used to allow the provider to be reused
|
||||||
|
// by the transport layer. As mentioned in [DefaultProvider] (provider nil, nil)
|
||||||
|
// may be returned to indicate a default provider could not be found, which
|
||||||
|
// will skip extra tls config in the transport layer .
|
||||||
|
type defaultCertData struct {
|
||||||
|
once sync.Once
|
||||||
|
provider Provider
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
defaultCert defaultCertData
|
||||||
|
)
|
||||||
|
|
||||||
|
// Provider is a function that can be passed into crypto/tls.Config.GetClientCertificate.
|
||||||
|
type Provider func(*tls.CertificateRequestInfo) (*tls.Certificate, error)
|
||||||
|
|
||||||
|
// errSourceUnavailable is a sentinel error to indicate certificate source is unavailable.
|
||||||
|
var errSourceUnavailable = errors.New("certificate source is unavailable")
|
||||||
|
|
||||||
|
// DefaultProvider returns a certificate source using the preferred EnterpriseCertificateProxySource.
|
||||||
|
// If EnterpriseCertificateProxySource is not available, fall back to the legacy SecureConnectSource.
|
||||||
|
//
|
||||||
|
// If neither source is available (due to missing configurations), a nil Source and a nil Error are
|
||||||
|
// returned to indicate that a default certificate source is unavailable.
|
||||||
|
func DefaultProvider() (Provider, error) {
|
||||||
|
defaultCert.once.Do(func() {
|
||||||
|
defaultCert.provider, defaultCert.err = NewWorkloadX509CertProvider("")
|
||||||
|
if errors.Is(defaultCert.err, errSourceUnavailable) {
|
||||||
|
defaultCert.provider, defaultCert.err = NewEnterpriseCertificateProxyProvider("")
|
||||||
|
if errors.Is(defaultCert.err, errSourceUnavailable) {
|
||||||
|
defaultCert.provider, defaultCert.err = NewSecureConnectProvider("")
|
||||||
|
if errors.Is(defaultCert.err, errSourceUnavailable) {
|
||||||
|
defaultCert.provider, defaultCert.err = nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return defaultCert.provider, defaultCert.err
|
||||||
|
}
|
||||||
54
vendor/cloud.google.com/go/auth/internal/transport/cert/enterprise_cert.go
generated
vendored
Normal file
54
vendor/cloud.google.com/go/auth/internal/transport/cert/enterprise_cert.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
|
||||||
|
"github.com/googleapis/enterprise-certificate-proxy/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ecpSource struct {
|
||||||
|
key *client.Key
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEnterpriseCertificateProxyProvider creates a certificate source
|
||||||
|
// using the Enterprise Certificate Proxy client, which delegates
|
||||||
|
// certifcate related operations to an OS-specific "signer binary"
|
||||||
|
// that communicates with the native keystore (ex. keychain on MacOS).
|
||||||
|
//
|
||||||
|
// The configFilePath points to a config file containing relevant parameters
|
||||||
|
// such as the certificate issuer and the location of the signer binary.
|
||||||
|
// If configFilePath is empty, the client will attempt to load the config from
|
||||||
|
// a well-known gcloud location.
|
||||||
|
func NewEnterpriseCertificateProxyProvider(configFilePath string) (Provider, error) {
|
||||||
|
key, err := client.Cred(configFilePath)
|
||||||
|
if err != nil {
|
||||||
|
// TODO(codyoss): once this is fixed upstream can handle this error a
|
||||||
|
// little better here. But be safe for now and assume unavailable.
|
||||||
|
return nil, errSourceUnavailable
|
||||||
|
}
|
||||||
|
|
||||||
|
return (&ecpSource{
|
||||||
|
key: key,
|
||||||
|
}).getClientCertificate, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ecpSource) getClientCertificate(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||||
|
var cert tls.Certificate
|
||||||
|
cert.PrivateKey = s.key
|
||||||
|
cert.Certificate = s.key.CertificateChain()
|
||||||
|
return &cert, nil
|
||||||
|
}
|
||||||
124
vendor/cloud.google.com/go/auth/internal/transport/cert/secureconnect_cert.go
generated
vendored
Normal file
124
vendor/cloud.google.com/go/auth/internal/transport/cert/secureconnect_cert.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
metadataPath = ".secureConnect"
|
||||||
|
metadataFile = "context_aware_metadata.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type secureConnectSource struct {
|
||||||
|
metadata secureConnectMetadata
|
||||||
|
|
||||||
|
// Cache the cert to avoid executing helper command repeatedly.
|
||||||
|
cachedCertMutex sync.Mutex
|
||||||
|
cachedCert *tls.Certificate
|
||||||
|
}
|
||||||
|
|
||||||
|
type secureConnectMetadata struct {
|
||||||
|
Cmd []string `json:"cert_provider_command"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSecureConnectProvider creates a certificate source using
|
||||||
|
// the Secure Connect Helper and its associated metadata file.
|
||||||
|
//
|
||||||
|
// The configFilePath points to the location of the context aware metadata file.
|
||||||
|
// If configFilePath is empty, use the default context aware metadata location.
|
||||||
|
func NewSecureConnectProvider(configFilePath string) (Provider, error) {
|
||||||
|
if configFilePath == "" {
|
||||||
|
user, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
// Error locating the default config means Secure Connect is not supported.
|
||||||
|
return nil, errSourceUnavailable
|
||||||
|
}
|
||||||
|
configFilePath = filepath.Join(user.HomeDir, metadataPath, metadataFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.ReadFile(configFilePath)
|
||||||
|
if err != nil {
|
||||||
|
// Config file missing means Secure Connect is not supported.
|
||||||
|
// There are non-os.ErrNotExist errors that may be returned.
|
||||||
|
// (e.g. if the home directory is /dev/null, *nix systems will
|
||||||
|
// return ENOTDIR instead of ENOENT)
|
||||||
|
return nil, errSourceUnavailable
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadata secureConnectMetadata
|
||||||
|
if err := json.Unmarshal(file, &metadata); err != nil {
|
||||||
|
return nil, fmt.Errorf("cert: could not parse JSON in %q: %w", configFilePath, err)
|
||||||
|
}
|
||||||
|
if err := validateMetadata(metadata); err != nil {
|
||||||
|
return nil, fmt.Errorf("cert: invalid config in %q: %w", configFilePath, err)
|
||||||
|
}
|
||||||
|
return (&secureConnectSource{
|
||||||
|
metadata: metadata,
|
||||||
|
}).getClientCertificate, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateMetadata(metadata secureConnectMetadata) error {
|
||||||
|
if len(metadata.Cmd) == 0 {
|
||||||
|
return errors.New("empty cert_provider_command")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *secureConnectSource) getClientCertificate(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||||
|
s.cachedCertMutex.Lock()
|
||||||
|
defer s.cachedCertMutex.Unlock()
|
||||||
|
if s.cachedCert != nil && !isCertificateExpired(s.cachedCert) {
|
||||||
|
return s.cachedCert, nil
|
||||||
|
}
|
||||||
|
// Expand OS environment variables in the cert provider command such as "$HOME".
|
||||||
|
for i := 0; i < len(s.metadata.Cmd); i++ {
|
||||||
|
s.metadata.Cmd[i] = os.ExpandEnv(s.metadata.Cmd[i])
|
||||||
|
}
|
||||||
|
command := s.metadata.Cmd
|
||||||
|
data, err := exec.Command(command[0], command[1:]...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cert, err := tls.X509KeyPair(data, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.cachedCert = &cert
|
||||||
|
return &cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isCertificateExpired returns true if the given cert is expired or invalid.
|
||||||
|
func isCertificateExpired(cert *tls.Certificate) bool {
|
||||||
|
if len(cert.Certificate) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
parsed, err := x509.ParseCertificate(cert.Certificate[0])
|
||||||
|
if err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return time.Now().After(parsed.NotAfter)
|
||||||
|
}
|
||||||
138
vendor/cloud.google.com/go/auth/internal/transport/cert/workload_cert.go
generated
vendored
Normal file
138
vendor/cloud.google.com/go/auth/internal/transport/cert/workload_cert.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
// Copyright 2024 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/googleapis/enterprise-certificate-proxy/client/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type certConfigs struct {
|
||||||
|
Workload *workloadSource `json:"workload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type workloadSource struct {
|
||||||
|
CertPath string `json:"cert_path"`
|
||||||
|
KeyPath string `json:"key_path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type certificateConfig struct {
|
||||||
|
CertConfigs certConfigs `json:"cert_configs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// getconfigFilePath determines the path to the certificate configuration file.
|
||||||
|
// It first checks for the presence of an environment variable that specifies
|
||||||
|
// the file path. If the environment variable is not set, it falls back to
|
||||||
|
// a default configuration file path.
|
||||||
|
func getconfigFilePath() string {
|
||||||
|
envFilePath := util.GetConfigFilePathFromEnv()
|
||||||
|
if envFilePath != "" {
|
||||||
|
return envFilePath
|
||||||
|
}
|
||||||
|
return util.GetDefaultConfigFilePath()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCertificatePath retrieves the certificate file path from the provided
|
||||||
|
// configuration file. If the configFilePath is empty, it attempts to load
|
||||||
|
// the configuration from a well-known gcloud location.
|
||||||
|
// This function is exposed to allow other packages, such as the
|
||||||
|
// externalaccount package, to retrieve the certificate path without needing
|
||||||
|
// to load the entire certificate configuration.
|
||||||
|
func GetCertificatePath(configFilePath string) (string, error) {
|
||||||
|
if configFilePath == "" {
|
||||||
|
configFilePath = getconfigFilePath()
|
||||||
|
}
|
||||||
|
certFile, _, err := getCertAndKeyFiles(configFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return certFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWorkloadX509CertProvider creates a certificate source
|
||||||
|
// that reads a certificate and private key file from the local file system.
|
||||||
|
// This is intended to be used for workload identity federation.
|
||||||
|
//
|
||||||
|
// The configFilePath points to a config file containing relevant parameters
|
||||||
|
// such as the certificate and key file paths.
|
||||||
|
// If configFilePath is empty, the client will attempt to load the config from
|
||||||
|
// a well-known gcloud location.
|
||||||
|
func NewWorkloadX509CertProvider(configFilePath string) (Provider, error) {
|
||||||
|
if configFilePath == "" {
|
||||||
|
configFilePath = getconfigFilePath()
|
||||||
|
}
|
||||||
|
certFile, keyFile, err := getCertAndKeyFiles(configFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
source := &workloadSource{
|
||||||
|
CertPath: certFile,
|
||||||
|
KeyPath: keyFile,
|
||||||
|
}
|
||||||
|
return source.getClientCertificate, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getClientCertificate attempts to load the certificate and key from the files specified in the
|
||||||
|
// certificate config.
|
||||||
|
func (s *workloadSource) getClientCertificate(info *tls.CertificateRequestInfo) (*tls.Certificate, error) {
|
||||||
|
cert, err := tls.LoadX509KeyPair(s.CertPath, s.KeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCertAndKeyFiles attempts to read the provided config file and return the certificate and private
|
||||||
|
// key file paths.
|
||||||
|
func getCertAndKeyFiles(configFilePath string) (string, string, error) {
|
||||||
|
jsonFile, err := os.Open(configFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", errSourceUnavailable
|
||||||
|
}
|
||||||
|
|
||||||
|
byteValue, err := io.ReadAll(jsonFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var config certificateConfig
|
||||||
|
if err := json.Unmarshal(byteValue, &config); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.CertConfigs.Workload == nil {
|
||||||
|
return "", "", errSourceUnavailable
|
||||||
|
}
|
||||||
|
|
||||||
|
certFile := config.CertConfigs.Workload.CertPath
|
||||||
|
keyFile := config.CertConfigs.Workload.KeyPath
|
||||||
|
|
||||||
|
if certFile == "" {
|
||||||
|
return "", "", errors.New("certificate configuration is missing the certificate file location")
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyFile == "" {
|
||||||
|
return "", "", errors.New("certificate configuration is missing the key file location")
|
||||||
|
}
|
||||||
|
|
||||||
|
return certFile, keyFile, nil
|
||||||
|
}
|
||||||
61
vendor/cloud.google.com/go/auth/internal/transport/headers/headers.go
generated
vendored
Normal file
61
vendor/cloud.google.com/go/auth/internal/transport/headers/headers.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright 2025 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package headers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetAuthHeader uses the provided token to set the Authorization and trust
|
||||||
|
// boundary headers on a request. If the token.Type is empty, the type is
|
||||||
|
// assumed to be Bearer.
|
||||||
|
func SetAuthHeader(token *auth.Token, req *http.Request) {
|
||||||
|
typ := token.Type
|
||||||
|
if typ == "" {
|
||||||
|
typ = internal.TokenTypeBearer
|
||||||
|
}
|
||||||
|
req.Header.Set("Authorization", typ+" "+token.Value)
|
||||||
|
|
||||||
|
if headerVal, setHeader := getTrustBoundaryHeader(token); setHeader {
|
||||||
|
req.Header.Set("x-allowed-locations", headerVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAuthMetadata uses the provided token to set the Authorization and trust
|
||||||
|
// boundary metadata. If the token.Type is empty, the type is assumed to be
|
||||||
|
// Bearer.
|
||||||
|
func SetAuthMetadata(token *auth.Token, m map[string]string) {
|
||||||
|
typ := token.Type
|
||||||
|
if typ == "" {
|
||||||
|
typ = internal.TokenTypeBearer
|
||||||
|
}
|
||||||
|
m["authorization"] = typ + " " + token.Value
|
||||||
|
|
||||||
|
if headerVal, setHeader := getTrustBoundaryHeader(token); setHeader {
|
||||||
|
m["x-allowed-locations"] = headerVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTrustBoundaryHeader(token *auth.Token) (val string, present bool) {
|
||||||
|
if data, ok := token.Metadata[internal.TrustBoundaryDataKey]; ok {
|
||||||
|
if tbd, ok := data.(internal.TrustBoundaryData); ok {
|
||||||
|
return tbd.TrustBoundaryHeader()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
138
vendor/cloud.google.com/go/auth/internal/transport/s2a.go
generated
vendored
Normal file
138
vendor/cloud.google.com/go/auth/internal/transport/s2a.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth/internal/transport/cert"
|
||||||
|
"cloud.google.com/go/compute/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
configEndpointSuffix = "instance/platform-security/auto-mtls-configuration"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mtlsConfiguration *mtlsConfig
|
||||||
|
|
||||||
|
mtlsOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetS2AAddress returns the S2A address to be reached via plaintext connection.
|
||||||
|
// Returns empty string if not set or invalid.
|
||||||
|
func GetS2AAddress(logger *slog.Logger) string {
|
||||||
|
getMetadataMTLSAutoConfig(logger)
|
||||||
|
if !mtlsConfiguration.valid() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return mtlsConfiguration.S2A.PlaintextAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMTLSS2AAddress returns the S2A address to be reached via MTLS connection.
|
||||||
|
// Returns empty string if not set or invalid.
|
||||||
|
func GetMTLSS2AAddress(logger *slog.Logger) string {
|
||||||
|
getMetadataMTLSAutoConfig(logger)
|
||||||
|
if !mtlsConfiguration.valid() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return mtlsConfiguration.S2A.MTLSAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
// mtlsConfig contains the configuration for establishing MTLS connections with Google APIs.
|
||||||
|
type mtlsConfig struct {
|
||||||
|
S2A *s2aAddresses `json:"s2a"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mtlsConfig) valid() bool {
|
||||||
|
return c != nil && c.S2A != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// s2aAddresses contains the plaintext and/or MTLS S2A addresses.
|
||||||
|
type s2aAddresses struct {
|
||||||
|
// PlaintextAddress is the plaintext address to reach S2A
|
||||||
|
PlaintextAddress string `json:"plaintext_address"`
|
||||||
|
// MTLSAddress is the MTLS address to reach S2A
|
||||||
|
MTLSAddress string `json:"mtls_address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMetadataMTLSAutoConfig(logger *slog.Logger) {
|
||||||
|
var err error
|
||||||
|
mtlsOnce.Do(func() {
|
||||||
|
mtlsConfiguration, err = queryConfig(logger)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Getting MTLS config failed: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpGetMetadataMTLSConfig = func(logger *slog.Logger) (string, error) {
|
||||||
|
metadataClient := metadata.NewWithOptions(&metadata.Options{
|
||||||
|
Logger: logger,
|
||||||
|
})
|
||||||
|
return metadataClient.GetWithContext(context.Background(), configEndpointSuffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryConfig(logger *slog.Logger) (*mtlsConfig, error) {
|
||||||
|
resp, err := httpGetMetadataMTLSConfig(logger)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("querying MTLS config from MDS endpoint failed: %w", err)
|
||||||
|
}
|
||||||
|
var config mtlsConfig
|
||||||
|
err = json.Unmarshal([]byte(resp), &config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unmarshalling MTLS config from MDS endpoint failed: %w", err)
|
||||||
|
}
|
||||||
|
if config.S2A == nil {
|
||||||
|
return nil, fmt.Errorf("returned MTLS config from MDS endpoint is invalid: %v", config)
|
||||||
|
}
|
||||||
|
return &config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldUseS2A(clientCertSource cert.Provider, opts *Options) bool {
|
||||||
|
// If client cert is found, use that over S2A.
|
||||||
|
if clientCertSource != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// If EXPERIMENTAL_GOOGLE_API_USE_S2A is not set to true, skip S2A.
|
||||||
|
if !isGoogleS2AEnabled() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// If DefaultMTLSEndpoint is not set or has endpoint override, skip S2A.
|
||||||
|
if opts.DefaultMTLSEndpoint == "" || opts.Endpoint != "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// If custom HTTP client is provided, skip S2A.
|
||||||
|
if opts.Client != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// If directPath is enabled, skip S2A.
|
||||||
|
return !opts.EnableDirectPath && !opts.EnableDirectPathXds
|
||||||
|
}
|
||||||
|
|
||||||
|
func isGoogleS2AEnabled() bool {
|
||||||
|
b, err := strconv.ParseBool(os.Getenv(googleAPIUseS2AEnv))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
107
vendor/cloud.google.com/go/auth/internal/transport/transport.go
generated
vendored
Normal file
107
vendor/cloud.google.com/go/auth/internal/transport/transport.go
generated
vendored
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package transport provided internal helpers for the two transport packages
|
||||||
|
// (grpctransport and httptransport).
|
||||||
|
package transport
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CloneDetectOptions clones a user set detect option into some new memory that
|
||||||
|
// we can internally manipulate before sending onto the detect package.
|
||||||
|
func CloneDetectOptions(oldDo *credentials.DetectOptions) *credentials.DetectOptions {
|
||||||
|
if oldDo == nil {
|
||||||
|
// it is valid for users not to set this, but we will need to to default
|
||||||
|
// some options for them in this case so return some initialized memory
|
||||||
|
// to work with.
|
||||||
|
return &credentials.DetectOptions{}
|
||||||
|
}
|
||||||
|
newDo := &credentials.DetectOptions{
|
||||||
|
// Simple types
|
||||||
|
TokenBindingType: oldDo.TokenBindingType,
|
||||||
|
Audience: oldDo.Audience,
|
||||||
|
Subject: oldDo.Subject,
|
||||||
|
EarlyTokenRefresh: oldDo.EarlyTokenRefresh,
|
||||||
|
TokenURL: oldDo.TokenURL,
|
||||||
|
STSAudience: oldDo.STSAudience,
|
||||||
|
CredentialsFile: oldDo.CredentialsFile,
|
||||||
|
UseSelfSignedJWT: oldDo.UseSelfSignedJWT,
|
||||||
|
UniverseDomain: oldDo.UniverseDomain,
|
||||||
|
|
||||||
|
// These fields are pointer types that we just want to use exactly as
|
||||||
|
// the user set, copy the ref
|
||||||
|
Client: oldDo.Client,
|
||||||
|
Logger: oldDo.Logger,
|
||||||
|
AuthHandlerOptions: oldDo.AuthHandlerOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smartly size this memory and copy below.
|
||||||
|
if len(oldDo.CredentialsJSON) > 0 {
|
||||||
|
newDo.CredentialsJSON = make([]byte, len(oldDo.CredentialsJSON))
|
||||||
|
copy(newDo.CredentialsJSON, oldDo.CredentialsJSON)
|
||||||
|
}
|
||||||
|
if len(oldDo.Scopes) > 0 {
|
||||||
|
newDo.Scopes = make([]string, len(oldDo.Scopes))
|
||||||
|
copy(newDo.Scopes, oldDo.Scopes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newDo
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateUniverseDomain verifies that the universe domain configured for the
|
||||||
|
// client matches the universe domain configured for the credentials.
|
||||||
|
func ValidateUniverseDomain(clientUniverseDomain, credentialsUniverseDomain string) error {
|
||||||
|
if clientUniverseDomain != credentialsUniverseDomain {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"the configured universe domain (%q) does not match the universe "+
|
||||||
|
"domain found in the credentials (%q). If you haven't configured "+
|
||||||
|
"the universe domain explicitly, \"googleapis.com\" is the default",
|
||||||
|
clientUniverseDomain,
|
||||||
|
credentialsUniverseDomain)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultHTTPClientWithTLS constructs an HTTPClient using the provided tlsConfig, to support mTLS.
|
||||||
|
func DefaultHTTPClientWithTLS(tlsConfig *tls.Config) *http.Client {
|
||||||
|
trans := BaseTransport()
|
||||||
|
trans.TLSClientConfig = tlsConfig
|
||||||
|
return &http.Client{Transport: trans}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseTransport returns a default [http.Transport] which can be used if
|
||||||
|
// [http.DefaultTransport] has been overwritten.
|
||||||
|
func BaseTransport() *http.Transport {
|
||||||
|
return &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
DialContext: (&net.Dialer{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
KeepAlive: 30 * time.Second,
|
||||||
|
DualStack: true,
|
||||||
|
}).DialContext,
|
||||||
|
MaxIdleConns: 100,
|
||||||
|
MaxIdleConnsPerHost: 100,
|
||||||
|
IdleConnTimeout: 90 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
100
vendor/cloud.google.com/go/auth/internal/trustboundary/external_accounts_config_providers.go
generated
vendored
Normal file
100
vendor/cloud.google.com/go/auth/internal/trustboundary/external_accounts_config_providers.go
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright 2025 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package trustboundary
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
workloadAllowedLocationsEndpoint = "https://iamcredentials.%s/v1/projects/%s/locations/global/workloadIdentityPools/%s/allowedLocations"
|
||||||
|
workforceAllowedLocationsEndpoint = "https://iamcredentials.%s/v1/locations/global/workforcePools/%s/allowedLocations"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
workforceAudiencePattern = regexp.MustCompile(`//iam\.([^/]+)/locations/global/workforcePools/([^/]+)`)
|
||||||
|
workloadAudiencePattern = regexp.MustCompile(`//iam\.([^/]+)/projects/([^/]+)/locations/global/workloadIdentityPools/([^/]+)`)
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewExternalAccountConfigProvider creates a new ConfigProvider for external accounts.
|
||||||
|
func NewExternalAccountConfigProvider(audience, inputUniverseDomain string) (ConfigProvider, error) {
|
||||||
|
var audienceDomain, projectNumber, poolID string
|
||||||
|
var isWorkload bool
|
||||||
|
|
||||||
|
matches := workloadAudiencePattern.FindStringSubmatch(audience)
|
||||||
|
if len(matches) == 4 { // Expecting full match, domain, projectNumber, poolID
|
||||||
|
audienceDomain = matches[1]
|
||||||
|
projectNumber = matches[2]
|
||||||
|
poolID = matches[3]
|
||||||
|
isWorkload = true
|
||||||
|
} else {
|
||||||
|
matches = workforceAudiencePattern.FindStringSubmatch(audience)
|
||||||
|
if len(matches) == 3 { // Expecting full match, domain, poolID
|
||||||
|
audienceDomain = matches[1]
|
||||||
|
poolID = matches[2]
|
||||||
|
isWorkload = false
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("trustboundary: unknown audience format: %q", audience)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
effectiveUniverseDomain := inputUniverseDomain
|
||||||
|
if effectiveUniverseDomain == "" {
|
||||||
|
effectiveUniverseDomain = audienceDomain
|
||||||
|
} else if audienceDomain != "" && effectiveUniverseDomain != audienceDomain {
|
||||||
|
return nil, fmt.Errorf("trustboundary: provided universe domain (%q) does not match domain in audience (%q)", inputUniverseDomain, audienceDomain)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isWorkload {
|
||||||
|
return &workloadIdentityPoolConfigProvider{
|
||||||
|
projectNumber: projectNumber,
|
||||||
|
poolID: poolID,
|
||||||
|
universeDomain: effectiveUniverseDomain,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return &workforcePoolConfigProvider{
|
||||||
|
poolID: poolID,
|
||||||
|
universeDomain: effectiveUniverseDomain,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type workforcePoolConfigProvider struct {
|
||||||
|
poolID string
|
||||||
|
universeDomain string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *workforcePoolConfigProvider) GetTrustBoundaryEndpoint(ctx context.Context) (string, error) {
|
||||||
|
return fmt.Sprintf(workforceAllowedLocationsEndpoint, p.universeDomain, p.poolID), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *workforcePoolConfigProvider) GetUniverseDomain(ctx context.Context) (string, error) {
|
||||||
|
return p.universeDomain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type workloadIdentityPoolConfigProvider struct {
|
||||||
|
projectNumber string
|
||||||
|
poolID string
|
||||||
|
universeDomain string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *workloadIdentityPoolConfigProvider) GetTrustBoundaryEndpoint(ctx context.Context) (string, error) {
|
||||||
|
return fmt.Sprintf(workloadAllowedLocationsEndpoint, p.universeDomain, p.projectNumber, p.poolID), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *workloadIdentityPoolConfigProvider) GetUniverseDomain(ctx context.Context) (string, error) {
|
||||||
|
return p.universeDomain, nil
|
||||||
|
}
|
||||||
392
vendor/cloud.google.com/go/auth/internal/trustboundary/trust_boundary.go
generated
vendored
Normal file
392
vendor/cloud.google.com/go/auth/internal/trustboundary/trust_boundary.go
generated
vendored
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
// Copyright 2025 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package trustboundary
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"cloud.google.com/go/auth/internal/retry"
|
||||||
|
"cloud.google.com/go/auth/internal/transport/headers"
|
||||||
|
"github.com/googleapis/gax-go/v2/internallog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// serviceAccountAllowedLocationsEndpoint is the URL for fetching allowed locations for a given service account email.
|
||||||
|
serviceAccountAllowedLocationsEndpoint = "https://iamcredentials.%s/v1/projects/-/serviceAccounts/%s/allowedLocations"
|
||||||
|
)
|
||||||
|
|
||||||
|
// isEnabled wraps isTrustBoundaryEnabled with sync.OnceValues to ensure it's
|
||||||
|
// called only once.
|
||||||
|
var isEnabled = sync.OnceValues(isTrustBoundaryEnabled)
|
||||||
|
|
||||||
|
// IsEnabled returns if the trust boundary feature is enabled and an error if
|
||||||
|
// the configuration is invalid. The underlying check is performed only once.
|
||||||
|
func IsEnabled() (bool, error) {
|
||||||
|
return isEnabled()
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTrustBoundaryEnabled checks if the trust boundary feature is enabled via
|
||||||
|
// GOOGLE_AUTH_TRUST_BOUNDARY_ENABLED environment variable.
|
||||||
|
//
|
||||||
|
// If the environment variable is not set, it is considered false.
|
||||||
|
//
|
||||||
|
// The environment variable is interpreted as a boolean with the following
|
||||||
|
// (case-insensitive) rules:
|
||||||
|
// - "true", "1" are considered true.
|
||||||
|
// - "false", "0" are considered false.
|
||||||
|
//
|
||||||
|
// Any other values will return an error.
|
||||||
|
func isTrustBoundaryEnabled() (bool, error) {
|
||||||
|
const envVar = "GOOGLE_AUTH_TRUST_BOUNDARY_ENABLED"
|
||||||
|
val, ok := os.LookupEnv(envVar)
|
||||||
|
if !ok {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
val = strings.ToLower(val)
|
||||||
|
switch val {
|
||||||
|
case "true", "1":
|
||||||
|
return true, nil
|
||||||
|
case "false", "0":
|
||||||
|
return false, nil
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf(`invalid value for %s: %q. Must be one of "true", "false", "1", or "0"`, envVar, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigProvider provides specific configuration for trust boundary lookups.
|
||||||
|
type ConfigProvider interface {
|
||||||
|
// GetTrustBoundaryEndpoint returns the endpoint URL for the trust boundary lookup.
|
||||||
|
GetTrustBoundaryEndpoint(ctx context.Context) (url string, err error)
|
||||||
|
// GetUniverseDomain returns the universe domain associated with the credential.
|
||||||
|
// It may return an error if the universe domain cannot be determined.
|
||||||
|
GetUniverseDomain(ctx context.Context) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowedLocationsResponse is the structure of the response from the Trust Boundary API.
|
||||||
|
type AllowedLocationsResponse struct {
|
||||||
|
// Locations is the list of allowed locations.
|
||||||
|
Locations []string `json:"locations"`
|
||||||
|
// EncodedLocations is the encoded representation of the allowed locations.
|
||||||
|
EncodedLocations string `json:"encodedLocations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchTrustBoundaryData fetches the trust boundary data from the API.
|
||||||
|
func fetchTrustBoundaryData(ctx context.Context, client *http.Client, url string, token *auth.Token, logger *slog.Logger) (*internal.TrustBoundaryData, error) {
|
||||||
|
if logger == nil {
|
||||||
|
logger = slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||||
|
}
|
||||||
|
if client == nil {
|
||||||
|
return nil, errors.New("trustboundary: HTTP client is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if url == "" {
|
||||||
|
return nil, errors.New("trustboundary: URL cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("trustboundary: failed to create trust boundary request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if token == nil || token.Value == "" {
|
||||||
|
return nil, errors.New("trustboundary: access token required for lookup API authentication")
|
||||||
|
}
|
||||||
|
headers.SetAuthHeader(token, req)
|
||||||
|
logger.DebugContext(ctx, "trust boundary request", "request", internallog.HTTPRequest(req, nil))
|
||||||
|
|
||||||
|
retryer := retry.New()
|
||||||
|
var response *http.Response
|
||||||
|
for {
|
||||||
|
response, err = client.Do(req)
|
||||||
|
|
||||||
|
var statusCode int
|
||||||
|
if response != nil {
|
||||||
|
statusCode = response.StatusCode
|
||||||
|
}
|
||||||
|
pause, shouldRetry := retryer.Retry(statusCode, err)
|
||||||
|
|
||||||
|
if !shouldRetry {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if response != nil {
|
||||||
|
// Drain and close the body to reuse the connection
|
||||||
|
io.Copy(io.Discard, response.Body)
|
||||||
|
response.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := retry.Sleep(ctx, pause); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("trustboundary: failed to fetch trust boundary: %w", err)
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
body, err := io.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("trustboundary: failed to read trust boundary response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.DebugContext(ctx, "trust boundary response", "response", internallog.HTTPResponse(response, body))
|
||||||
|
|
||||||
|
if response.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("trustboundary: trust boundary request failed with status: %s, body: %s", response.Status, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
apiResponse := AllowedLocationsResponse{}
|
||||||
|
if err := json.Unmarshal(body, &apiResponse); err != nil {
|
||||||
|
return nil, fmt.Errorf("trustboundary: failed to unmarshal trust boundary response: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if apiResponse.EncodedLocations == "" {
|
||||||
|
return nil, errors.New("trustboundary: invalid API response: encodedLocations is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return internal.NewTrustBoundaryData(apiResponse.Locations, apiResponse.EncodedLocations), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// serviceAccountConfig holds configuration for SA trust boundary lookups.
|
||||||
|
// It implements the ConfigProvider interface.
|
||||||
|
type serviceAccountConfig struct {
|
||||||
|
ServiceAccountEmail string
|
||||||
|
UniverseDomain string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServiceAccountConfigProvider creates a new config for service accounts.
|
||||||
|
func NewServiceAccountConfigProvider(saEmail, universeDomain string) ConfigProvider {
|
||||||
|
return &serviceAccountConfig{
|
||||||
|
ServiceAccountEmail: saEmail,
|
||||||
|
UniverseDomain: universeDomain,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTrustBoundaryEndpoint returns the formatted URL for fetching allowed locations
|
||||||
|
// for the configured service account and universe domain.
|
||||||
|
func (sac *serviceAccountConfig) GetTrustBoundaryEndpoint(ctx context.Context) (url string, err error) {
|
||||||
|
if sac.ServiceAccountEmail == "" {
|
||||||
|
return "", errors.New("trustboundary: service account email cannot be empty for config")
|
||||||
|
}
|
||||||
|
ud := sac.UniverseDomain
|
||||||
|
if ud == "" {
|
||||||
|
ud = internal.DefaultUniverseDomain
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(serviceAccountAllowedLocationsEndpoint, ud, sac.ServiceAccountEmail), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUniverseDomain returns the configured universe domain, defaulting to
|
||||||
|
// [internal.DefaultUniverseDomain] if not explicitly set.
|
||||||
|
func (sac *serviceAccountConfig) GetUniverseDomain(ctx context.Context) (string, error) {
|
||||||
|
if sac.UniverseDomain == "" {
|
||||||
|
return internal.DefaultUniverseDomain, nil
|
||||||
|
}
|
||||||
|
return sac.UniverseDomain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DataProvider fetches and caches trust boundary Data.
|
||||||
|
// It implements the DataProvider interface and uses a ConfigProvider
|
||||||
|
// to get type-specific details for the lookup.
|
||||||
|
type DataProvider struct {
|
||||||
|
client *http.Client
|
||||||
|
configProvider ConfigProvider
|
||||||
|
data *internal.TrustBoundaryData
|
||||||
|
logger *slog.Logger
|
||||||
|
base auth.TokenProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProvider wraps the provided base [auth.TokenProvider] to create a new
|
||||||
|
// provider that injects tokens with trust boundary data. It uses the provided
|
||||||
|
// HTTP client and configProvider to fetch the data and attach it to the token's
|
||||||
|
// metadata.
|
||||||
|
func NewProvider(client *http.Client, configProvider ConfigProvider, logger *slog.Logger, base auth.TokenProvider) (*DataProvider, error) {
|
||||||
|
if client == nil {
|
||||||
|
return nil, errors.New("trustboundary: HTTP client cannot be nil for DataProvider")
|
||||||
|
}
|
||||||
|
if configProvider == nil {
|
||||||
|
return nil, errors.New("trustboundary: ConfigProvider cannot be nil for DataProvider")
|
||||||
|
}
|
||||||
|
p := &DataProvider{
|
||||||
|
client: client,
|
||||||
|
configProvider: configProvider,
|
||||||
|
logger: internallog.New(logger),
|
||||||
|
base: base,
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token retrieves a token from the base provider and injects it with trust
|
||||||
|
// boundary data.
|
||||||
|
func (p *DataProvider) Token(ctx context.Context) (*auth.Token, error) {
|
||||||
|
// Get the original token.
|
||||||
|
token, err := p.base.Token(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tbData, err := p.GetTrustBoundaryData(ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("trustboundary: error fetching the trust boundary data: %w", err)
|
||||||
|
}
|
||||||
|
if tbData != nil {
|
||||||
|
if token.Metadata == nil {
|
||||||
|
token.Metadata = make(map[string]interface{})
|
||||||
|
}
|
||||||
|
token.Metadata[internal.TrustBoundaryDataKey] = *tbData
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTrustBoundaryData retrieves the trust boundary data.
|
||||||
|
// It first checks the universe domain: if it's non-default, a NoOp is returned.
|
||||||
|
// Otherwise, it checks a local cache. If the data is not cached as NoOp,
|
||||||
|
// it fetches new data from the endpoint provided by its ConfigProvider,
|
||||||
|
// using the given accessToken for authentication. Results are cached.
|
||||||
|
// If fetching fails, it returns previously cached data if available, otherwise the fetch error.
|
||||||
|
func (p *DataProvider) GetTrustBoundaryData(ctx context.Context, token *auth.Token) (*internal.TrustBoundaryData, error) {
|
||||||
|
// Check the universe domain.
|
||||||
|
uniDomain, err := p.configProvider.GetUniverseDomain(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("trustboundary: error getting universe domain: %w", err)
|
||||||
|
}
|
||||||
|
if uniDomain != "" && uniDomain != internal.DefaultUniverseDomain {
|
||||||
|
if p.data == nil || p.data.EncodedLocations != internal.TrustBoundaryNoOp {
|
||||||
|
p.data = internal.NewNoOpTrustBoundaryData()
|
||||||
|
}
|
||||||
|
return p.data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check cache for a no-op result from a previous API call.
|
||||||
|
cachedData := p.data
|
||||||
|
if cachedData != nil && cachedData.EncodedLocations == internal.TrustBoundaryNoOp {
|
||||||
|
return cachedData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the endpoint
|
||||||
|
url, err := p.configProvider.GetTrustBoundaryEndpoint(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("trustboundary: error getting the lookup endpoint: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proceed to fetch new data.
|
||||||
|
newData, fetchErr := fetchTrustBoundaryData(ctx, p.client, url, token, p.logger)
|
||||||
|
|
||||||
|
if fetchErr != nil {
|
||||||
|
// Fetch failed. Fallback to cachedData if available.
|
||||||
|
if cachedData != nil {
|
||||||
|
return cachedData, nil // Successful fallback
|
||||||
|
}
|
||||||
|
// No cache to fallback to.
|
||||||
|
return nil, fmt.Errorf("trustboundary: failed to fetch trust boundary data for endpoint %s and no cache available: %w", url, fetchErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch successful. Update cache.
|
||||||
|
p.data = newData
|
||||||
|
return newData, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GCEConfigProvider implements ConfigProvider for GCE environments.
|
||||||
|
// It lazily fetches and caches the necessary metadata (service account email, universe domain)
|
||||||
|
// from the GCE metadata server.
|
||||||
|
type GCEConfigProvider struct {
|
||||||
|
// universeDomainProvider provides the universe domain and underlying metadata client.
|
||||||
|
universeDomainProvider *internal.ComputeUniverseDomainProvider
|
||||||
|
|
||||||
|
// Caching for service account email
|
||||||
|
saOnce sync.Once
|
||||||
|
saEmail string
|
||||||
|
saEmailErr error
|
||||||
|
|
||||||
|
// Caching for universe domain
|
||||||
|
udOnce sync.Once
|
||||||
|
ud string
|
||||||
|
udErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGCEConfigProvider creates a new GCEConfigProvider
|
||||||
|
// which uses the provided gceUDP to interact with the GCE metadata server.
|
||||||
|
func NewGCEConfigProvider(gceUDP *internal.ComputeUniverseDomainProvider) *GCEConfigProvider {
|
||||||
|
// The validity of gceUDP and its internal MetadataClient will be checked
|
||||||
|
// within the GetTrustBoundaryEndpoint and GetUniverseDomain methods.
|
||||||
|
return &GCEConfigProvider{
|
||||||
|
universeDomainProvider: gceUDP,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GCEConfigProvider) fetchSA(ctx context.Context) {
|
||||||
|
if g.universeDomainProvider == nil || g.universeDomainProvider.MetadataClient == nil {
|
||||||
|
g.saEmailErr = errors.New("trustboundary: GCEConfigProvider not properly initialized (missing ComputeUniverseDomainProvider or MetadataClient)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mdClient := g.universeDomainProvider.MetadataClient
|
||||||
|
saEmail, err := mdClient.EmailWithContext(ctx, "default")
|
||||||
|
if err != nil {
|
||||||
|
g.saEmailErr = fmt.Errorf("trustboundary: GCE config: failed to get service account email: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.saEmail = saEmail
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GCEConfigProvider) fetchUD(ctx context.Context) {
|
||||||
|
if g.universeDomainProvider == nil || g.universeDomainProvider.MetadataClient == nil {
|
||||||
|
g.udErr = errors.New("trustboundary: GCEConfigProvider not properly initialized (missing ComputeUniverseDomainProvider or MetadataClient)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ud, err := g.universeDomainProvider.GetProperty(ctx)
|
||||||
|
if err != nil {
|
||||||
|
g.udErr = fmt.Errorf("trustboundary: GCE config: failed to get universe domain: %w", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ud == "" {
|
||||||
|
ud = internal.DefaultUniverseDomain
|
||||||
|
}
|
||||||
|
g.ud = ud
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTrustBoundaryEndpoint constructs the trust boundary lookup URL for a GCE environment.
|
||||||
|
// It uses cached metadata (service account email, universe domain) after the first call.
|
||||||
|
func (g *GCEConfigProvider) GetTrustBoundaryEndpoint(ctx context.Context) (string, error) {
|
||||||
|
g.saOnce.Do(func() { g.fetchSA(ctx) })
|
||||||
|
if g.saEmailErr != nil {
|
||||||
|
return "", g.saEmailErr
|
||||||
|
}
|
||||||
|
g.udOnce.Do(func() { g.fetchUD(ctx) })
|
||||||
|
if g.udErr != nil {
|
||||||
|
return "", g.udErr
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(serviceAccountAllowedLocationsEndpoint, g.ud, g.saEmail), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUniverseDomain retrieves the universe domain from the GCE metadata server.
|
||||||
|
// It uses a cached value after the first call.
|
||||||
|
func (g *GCEConfigProvider) GetUniverseDomain(ctx context.Context) (string, error) {
|
||||||
|
g.udOnce.Do(func() { g.fetchUD(ctx) })
|
||||||
|
if g.udErr != nil {
|
||||||
|
return "", g.udErr
|
||||||
|
}
|
||||||
|
return g.ud, nil
|
||||||
|
}
|
||||||
20
vendor/cloud.google.com/go/auth/internal/version.go
generated
vendored
Normal file
20
vendor/cloud.google.com/go/auth/internal/version.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2026 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Code generated by gapicgen. DO NOT EDIT.
|
||||||
|
|
||||||
|
package internal
|
||||||
|
|
||||||
|
// Version is the current tagged release of the library.
|
||||||
|
const Version = "0.18.1"
|
||||||
82
vendor/cloud.google.com/go/auth/oauth2adapt/CHANGES.md
generated
vendored
Normal file
82
vendor/cloud.google.com/go/auth/oauth2adapt/CHANGES.md
generated
vendored
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [0.2.8](https://github.com/googleapis/google-cloud-go/compare/auth/oauth2adapt/v0.2.7...auth/oauth2adapt/v0.2.8) (2025-03-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Update golang.org/x/net to 0.37.0 ([1144978](https://github.com/googleapis/google-cloud-go/commit/11449782c7fb4896bf8b8b9cde8e7441c84fb2fd))
|
||||||
|
|
||||||
|
## [0.2.7](https://github.com/googleapis/google-cloud-go/compare/auth/oauth2adapt/v0.2.6...auth/oauth2adapt/v0.2.7) (2025-01-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Update golang.org/x/net to v0.33.0 ([e9b0b69](https://github.com/googleapis/google-cloud-go/commit/e9b0b69644ea5b276cacff0a707e8a5e87efafc9))
|
||||||
|
|
||||||
|
## [0.2.6](https://github.com/googleapis/google-cloud-go/compare/auth/oauth2adapt/v0.2.5...auth/oauth2adapt/v0.2.6) (2024-11-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Copy map in tokenSourceAdapter.Token ([#11164](https://github.com/googleapis/google-cloud-go/issues/11164)) ([8cb0cbc](https://github.com/googleapis/google-cloud-go/commit/8cb0cbccdc32886dfb3af49fee04012937d114d2)), refs [#11161](https://github.com/googleapis/google-cloud-go/issues/11161)
|
||||||
|
|
||||||
|
## [0.2.5](https://github.com/googleapis/google-cloud-go/compare/auth/oauth2adapt/v0.2.4...auth/oauth2adapt/v0.2.5) (2024-10-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Convert token metadata where possible ([#11062](https://github.com/googleapis/google-cloud-go/issues/11062)) ([34bf1c1](https://github.com/googleapis/google-cloud-go/commit/34bf1c164465d66745c0cfdf7cd10a8e2da92e52))
|
||||||
|
|
||||||
|
## [0.2.4](https://github.com/googleapis/google-cloud-go/compare/auth/oauth2adapt/v0.2.3...auth/oauth2adapt/v0.2.4) (2024-08-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Update dependencies ([257c40b](https://github.com/googleapis/google-cloud-go/commit/257c40bd6d7e59730017cf32bda8823d7a232758))
|
||||||
|
|
||||||
|
## [0.2.3](https://github.com/googleapis/google-cloud-go/compare/auth/oauth2adapt/v0.2.2...auth/oauth2adapt/v0.2.3) (2024-07-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Bump google.golang.org/api@v0.187.0 ([8fa9e39](https://github.com/googleapis/google-cloud-go/commit/8fa9e398e512fd8533fd49060371e61b5725a85b))
|
||||||
|
|
||||||
|
## [0.2.2](https://github.com/googleapis/google-cloud-go/compare/auth/oauth2adapt/v0.2.1...auth/oauth2adapt/v0.2.2) (2024-04-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Bump x/net to v0.24.0 ([ba31ed5](https://github.com/googleapis/google-cloud-go/commit/ba31ed5fda2c9664f2e1cf972469295e63deb5b4))
|
||||||
|
|
||||||
|
## [0.2.1](https://github.com/googleapis/google-cloud-go/compare/auth/oauth2adapt/v0.2.0...auth/oauth2adapt/v0.2.1) (2024-04-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Adapt Token Types to be translated ([#9801](https://github.com/googleapis/google-cloud-go/issues/9801)) ([70f4115](https://github.com/googleapis/google-cloud-go/commit/70f411555ebbf2b71e6d425cc8d2030644c6b438)), refs [#9800](https://github.com/googleapis/google-cloud-go/issues/9800)
|
||||||
|
|
||||||
|
## [0.2.0](https://github.com/googleapis/google-cloud-go/compare/auth/oauth2adapt/v0.1.0...auth/oauth2adapt/v0.2.0) (2024-04-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Add helpers for working with credentials types ([#9694](https://github.com/googleapis/google-cloud-go/issues/9694)) ([cf33b55](https://github.com/googleapis/google-cloud-go/commit/cf33b5514423a2ac5c2a323a1cd99aac34fd4233))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Update protobuf dep to v1.33.0 ([30b038d](https://github.com/googleapis/google-cloud-go/commit/30b038d8cac0b8cd5dd4761c87f3f298760dd33a))
|
||||||
|
|
||||||
|
## 0.1.0 (2023-10-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Adds a new module to translate types ([#8595](https://github.com/googleapis/google-cloud-go/issues/8595)) ([6933c5a](https://github.com/googleapis/google-cloud-go/commit/6933c5a0c1fc8e58cbfff8bbca439d671b94672f))
|
||||||
|
* **auth/oauth2adapt:** Fixup deps for release ([#8747](https://github.com/googleapis/google-cloud-go/issues/8747)) ([749d243](https://github.com/googleapis/google-cloud-go/commit/749d243862b025a6487a4d2d339219889b4cfe70))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **auth/oauth2adapt:** Update golang.org/x/net to v0.17.0 ([174da47](https://github.com/googleapis/google-cloud-go/commit/174da47254fefb12921bbfc65b7829a453af6f5d))
|
||||||
202
vendor/cloud.google.com/go/auth/oauth2adapt/LICENSE
generated
vendored
Normal file
202
vendor/cloud.google.com/go/auth/oauth2adapt/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
200
vendor/cloud.google.com/go/auth/oauth2adapt/oauth2adapt.go
generated
vendored
Normal file
200
vendor/cloud.google.com/go/auth/oauth2adapt/oauth2adapt.go
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package oauth2adapt helps converts types used in [cloud.google.com/go/auth]
|
||||||
|
// and [golang.org/x/oauth2].
|
||||||
|
package oauth2adapt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/oauth2/google"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
oauth2TokenSourceKey = "oauth2.google.tokenSource"
|
||||||
|
oauth2ServiceAccountKey = "oauth2.google.serviceAccount"
|
||||||
|
authTokenSourceKey = "auth.google.tokenSource"
|
||||||
|
authServiceAccountKey = "auth.google.serviceAccount"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TokenProviderFromTokenSource converts any [golang.org/x/oauth2.TokenSource]
|
||||||
|
// into a [cloud.google.com/go/auth.TokenProvider].
|
||||||
|
func TokenProviderFromTokenSource(ts oauth2.TokenSource) auth.TokenProvider {
|
||||||
|
return &tokenProviderAdapter{ts: ts}
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenProviderAdapter struct {
|
||||||
|
ts oauth2.TokenSource
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token fulfills the [cloud.google.com/go/auth.TokenProvider] interface. It
|
||||||
|
// is a light wrapper around the underlying TokenSource.
|
||||||
|
func (tp *tokenProviderAdapter) Token(context.Context) (*auth.Token, error) {
|
||||||
|
tok, err := tp.ts.Token()
|
||||||
|
if err != nil {
|
||||||
|
var err2 *oauth2.RetrieveError
|
||||||
|
if ok := errors.As(err, &err2); ok {
|
||||||
|
return nil, AuthErrorFromRetrieveError(err2)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Preserve compute token metadata, for both types of tokens.
|
||||||
|
metadata := map[string]interface{}{}
|
||||||
|
if val, ok := tok.Extra(oauth2TokenSourceKey).(string); ok {
|
||||||
|
metadata[authTokenSourceKey] = val
|
||||||
|
metadata[oauth2TokenSourceKey] = val
|
||||||
|
}
|
||||||
|
if val, ok := tok.Extra(oauth2ServiceAccountKey).(string); ok {
|
||||||
|
metadata[authServiceAccountKey] = val
|
||||||
|
metadata[oauth2ServiceAccountKey] = val
|
||||||
|
}
|
||||||
|
return &auth.Token{
|
||||||
|
Value: tok.AccessToken,
|
||||||
|
Type: tok.Type(),
|
||||||
|
Expiry: tok.Expiry,
|
||||||
|
Metadata: metadata,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenSourceFromTokenProvider converts any
|
||||||
|
// [cloud.google.com/go/auth.TokenProvider] into a
|
||||||
|
// [golang.org/x/oauth2.TokenSource].
|
||||||
|
func TokenSourceFromTokenProvider(tp auth.TokenProvider) oauth2.TokenSource {
|
||||||
|
return &tokenSourceAdapter{tp: tp}
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenSourceAdapter struct {
|
||||||
|
tp auth.TokenProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token fulfills the [golang.org/x/oauth2.TokenSource] interface. It
|
||||||
|
// is a light wrapper around the underlying TokenProvider.
|
||||||
|
func (ts *tokenSourceAdapter) Token() (*oauth2.Token, error) {
|
||||||
|
tok, err := ts.tp.Token(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
var err2 *auth.Error
|
||||||
|
if ok := errors.As(err, &err2); ok {
|
||||||
|
return nil, AddRetrieveErrorToAuthError(err2)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tok2 := &oauth2.Token{
|
||||||
|
AccessToken: tok.Value,
|
||||||
|
TokenType: tok.Type,
|
||||||
|
Expiry: tok.Expiry,
|
||||||
|
}
|
||||||
|
// Preserve token metadata.
|
||||||
|
m := tok.Metadata
|
||||||
|
if m != nil {
|
||||||
|
// Copy map to avoid concurrent map writes error (#11161).
|
||||||
|
metadata := make(map[string]interface{}, len(m)+2)
|
||||||
|
for k, v := range m {
|
||||||
|
metadata[k] = v
|
||||||
|
}
|
||||||
|
// Append compute token metadata in converted form.
|
||||||
|
if val, ok := metadata[authTokenSourceKey].(string); ok && val != "" {
|
||||||
|
metadata[oauth2TokenSourceKey] = val
|
||||||
|
}
|
||||||
|
if val, ok := metadata[authServiceAccountKey].(string); ok && val != "" {
|
||||||
|
metadata[oauth2ServiceAccountKey] = val
|
||||||
|
}
|
||||||
|
tok2 = tok2.WithExtra(metadata)
|
||||||
|
}
|
||||||
|
return tok2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthCredentialsFromOauth2Credentials converts a [golang.org/x/oauth2/google.Credentials]
|
||||||
|
// to a [cloud.google.com/go/auth.Credentials].
|
||||||
|
func AuthCredentialsFromOauth2Credentials(creds *google.Credentials) *auth.Credentials {
|
||||||
|
if creds == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return auth.NewCredentials(&auth.CredentialsOptions{
|
||||||
|
TokenProvider: TokenProviderFromTokenSource(creds.TokenSource),
|
||||||
|
JSON: creds.JSON,
|
||||||
|
ProjectIDProvider: auth.CredentialsPropertyFunc(func(ctx context.Context) (string, error) {
|
||||||
|
return creds.ProjectID, nil
|
||||||
|
}),
|
||||||
|
UniverseDomainProvider: auth.CredentialsPropertyFunc(func(ctx context.Context) (string, error) {
|
||||||
|
return creds.GetUniverseDomain()
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Oauth2CredentialsFromAuthCredentials converts a [cloud.google.com/go/auth.Credentials]
|
||||||
|
// to a [golang.org/x/oauth2/google.Credentials].
|
||||||
|
func Oauth2CredentialsFromAuthCredentials(creds *auth.Credentials) *google.Credentials {
|
||||||
|
if creds == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Throw away errors as old credentials are not request aware. Also, no
|
||||||
|
// network requests are currently happening for this use case.
|
||||||
|
projectID, _ := creds.ProjectID(context.Background())
|
||||||
|
|
||||||
|
return &google.Credentials{
|
||||||
|
TokenSource: TokenSourceFromTokenProvider(creds.TokenProvider),
|
||||||
|
ProjectID: projectID,
|
||||||
|
JSON: creds.JSON(),
|
||||||
|
UniverseDomainProvider: func() (string, error) {
|
||||||
|
return creds.UniverseDomain(context.Background())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type oauth2Error struct {
|
||||||
|
ErrorCode string `json:"error"`
|
||||||
|
ErrorDescription string `json:"error_description"`
|
||||||
|
ErrorURI string `json:"error_uri"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddRetrieveErrorToAuthError returns the same error provided and adds a
|
||||||
|
// [golang.org/x/oauth2.RetrieveError] to the error chain by setting the `Err` field on the
|
||||||
|
// [cloud.google.com/go/auth.Error].
|
||||||
|
func AddRetrieveErrorToAuthError(err *auth.Error) *auth.Error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
e := &oauth2.RetrieveError{
|
||||||
|
Response: err.Response,
|
||||||
|
Body: err.Body,
|
||||||
|
}
|
||||||
|
err.Err = e
|
||||||
|
if len(err.Body) > 0 {
|
||||||
|
var oErr oauth2Error
|
||||||
|
// ignore the error as it only fills in extra details
|
||||||
|
json.Unmarshal(err.Body, &oErr)
|
||||||
|
e.ErrorCode = oErr.ErrorCode
|
||||||
|
e.ErrorDescription = oErr.ErrorDescription
|
||||||
|
e.ErrorURI = oErr.ErrorURI
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthErrorFromRetrieveError returns an [cloud.google.com/go/auth.Error] that
|
||||||
|
// wraps the provided [golang.org/x/oauth2.RetrieveError].
|
||||||
|
func AuthErrorFromRetrieveError(err *oauth2.RetrieveError) *auth.Error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &auth.Error{
|
||||||
|
Response: err.Response,
|
||||||
|
Body: err.Body,
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
382
vendor/cloud.google.com/go/auth/threelegged.go
generated
vendored
Normal file
382
vendor/cloud.google.com/go/auth/threelegged.go
generated
vendored
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"mime"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"cloud.google.com/go/auth/internal"
|
||||||
|
"github.com/googleapis/gax-go/v2/internallog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AuthorizationHandler is a 3-legged-OAuth helper that prompts the user for
|
||||||
|
// OAuth consent at the specified auth code URL and returns an auth code and
|
||||||
|
// state upon approval.
|
||||||
|
type AuthorizationHandler func(authCodeURL string) (code string, state string, err error)
|
||||||
|
|
||||||
|
// Options3LO are the options for doing a 3-legged OAuth2 flow.
|
||||||
|
type Options3LO struct {
|
||||||
|
// ClientID is the application's ID.
|
||||||
|
ClientID string
|
||||||
|
// ClientSecret is the application's secret. Not required if AuthHandlerOpts
|
||||||
|
// is set.
|
||||||
|
ClientSecret string
|
||||||
|
// AuthURL is the URL for authenticating.
|
||||||
|
AuthURL string
|
||||||
|
// TokenURL is the URL for retrieving a token.
|
||||||
|
TokenURL string
|
||||||
|
// AuthStyle is used to describe how to client info in the token request.
|
||||||
|
AuthStyle Style
|
||||||
|
// RefreshToken is the token used to refresh the credential. Not required
|
||||||
|
// if AuthHandlerOpts is set.
|
||||||
|
RefreshToken string
|
||||||
|
// RedirectURL is the URL to redirect users to. Optional.
|
||||||
|
RedirectURL string
|
||||||
|
// Scopes specifies requested permissions for the Token. Optional.
|
||||||
|
Scopes []string
|
||||||
|
|
||||||
|
// URLParams are the set of values to apply to the token exchange. Optional.
|
||||||
|
URLParams url.Values
|
||||||
|
// Client is the client to be used to make the underlying token requests.
|
||||||
|
// Optional.
|
||||||
|
Client *http.Client
|
||||||
|
// EarlyTokenExpiry is the time before the token expires that it should be
|
||||||
|
// refreshed. If not set the default value is 3 minutes and 45 seconds.
|
||||||
|
// Optional.
|
||||||
|
EarlyTokenExpiry time.Duration
|
||||||
|
|
||||||
|
// AuthHandlerOpts provides a set of options for doing a
|
||||||
|
// 3-legged OAuth2 flow with a custom [AuthorizationHandler]. Optional.
|
||||||
|
AuthHandlerOpts *AuthorizationHandlerOptions
|
||||||
|
// Logger is used for debug logging. If provided, logging will be enabled
|
||||||
|
// at the loggers configured level. By default logging is disabled unless
|
||||||
|
// enabled by setting GOOGLE_SDK_GO_LOGGING_LEVEL in which case a default
|
||||||
|
// logger will be used. Optional.
|
||||||
|
Logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options3LO) validate() error {
|
||||||
|
if o == nil {
|
||||||
|
return errors.New("auth: options must be provided")
|
||||||
|
}
|
||||||
|
if o.ClientID == "" {
|
||||||
|
return errors.New("auth: client ID must be provided")
|
||||||
|
}
|
||||||
|
if o.AuthHandlerOpts == nil && o.ClientSecret == "" {
|
||||||
|
return errors.New("auth: client secret must be provided")
|
||||||
|
}
|
||||||
|
if o.AuthURL == "" {
|
||||||
|
return errors.New("auth: auth URL must be provided")
|
||||||
|
}
|
||||||
|
if o.TokenURL == "" {
|
||||||
|
return errors.New("auth: token URL must be provided")
|
||||||
|
}
|
||||||
|
if o.AuthStyle == StyleUnknown {
|
||||||
|
return errors.New("auth: auth style must be provided")
|
||||||
|
}
|
||||||
|
if o.AuthHandlerOpts == nil && o.RefreshToken == "" {
|
||||||
|
return errors.New("auth: refresh token must be provided")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options3LO) logger() *slog.Logger {
|
||||||
|
return internallog.New(o.Logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PKCEOptions holds parameters to support PKCE.
|
||||||
|
type PKCEOptions struct {
|
||||||
|
// Challenge is the un-padded, base64-url-encoded string of the encrypted code verifier.
|
||||||
|
Challenge string // The un-padded, base64-url-encoded string of the encrypted code verifier.
|
||||||
|
// ChallengeMethod is the encryption method (ex. S256).
|
||||||
|
ChallengeMethod string
|
||||||
|
// Verifier is the original, non-encrypted secret.
|
||||||
|
Verifier string // The original, non-encrypted secret.
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenJSON struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
RefreshToken string `json:"refresh_token"`
|
||||||
|
ExpiresIn int `json:"expires_in"`
|
||||||
|
// error fields
|
||||||
|
ErrorCode string `json:"error"`
|
||||||
|
ErrorDescription string `json:"error_description"`
|
||||||
|
ErrorURI string `json:"error_uri"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *tokenJSON) expiry() (t time.Time) {
|
||||||
|
if v := e.ExpiresIn; v != 0 {
|
||||||
|
return time.Now().Add(time.Duration(v) * time.Second)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options3LO) client() *http.Client {
|
||||||
|
if o.Client != nil {
|
||||||
|
return o.Client
|
||||||
|
}
|
||||||
|
return internal.DefaultClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
// authCodeURL returns a URL that points to a OAuth2 consent page.
|
||||||
|
func (o *Options3LO) authCodeURL(state string, values url.Values) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
buf.WriteString(o.AuthURL)
|
||||||
|
v := url.Values{
|
||||||
|
"response_type": {"code"},
|
||||||
|
"client_id": {o.ClientID},
|
||||||
|
}
|
||||||
|
if o.RedirectURL != "" {
|
||||||
|
v.Set("redirect_uri", o.RedirectURL)
|
||||||
|
}
|
||||||
|
if len(o.Scopes) > 0 {
|
||||||
|
v.Set("scope", strings.Join(o.Scopes, " "))
|
||||||
|
}
|
||||||
|
if state != "" {
|
||||||
|
v.Set("state", state)
|
||||||
|
}
|
||||||
|
if o.AuthHandlerOpts != nil {
|
||||||
|
if o.AuthHandlerOpts.PKCEOpts != nil &&
|
||||||
|
o.AuthHandlerOpts.PKCEOpts.Challenge != "" {
|
||||||
|
v.Set(codeChallengeKey, o.AuthHandlerOpts.PKCEOpts.Challenge)
|
||||||
|
}
|
||||||
|
if o.AuthHandlerOpts.PKCEOpts != nil &&
|
||||||
|
o.AuthHandlerOpts.PKCEOpts.ChallengeMethod != "" {
|
||||||
|
v.Set(codeChallengeMethodKey, o.AuthHandlerOpts.PKCEOpts.ChallengeMethod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for k := range values {
|
||||||
|
v.Set(k, v.Get(k))
|
||||||
|
}
|
||||||
|
if strings.Contains(o.AuthURL, "?") {
|
||||||
|
buf.WriteByte('&')
|
||||||
|
} else {
|
||||||
|
buf.WriteByte('?')
|
||||||
|
}
|
||||||
|
buf.WriteString(v.Encode())
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// New3LOTokenProvider returns a [TokenProvider] based on the 3-legged OAuth2
|
||||||
|
// configuration. The TokenProvider is caches and auto-refreshes tokens by
|
||||||
|
// default.
|
||||||
|
func New3LOTokenProvider(opts *Options3LO) (TokenProvider, error) {
|
||||||
|
if err := opts.validate(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if opts.AuthHandlerOpts != nil {
|
||||||
|
return new3LOTokenProviderWithAuthHandler(opts), nil
|
||||||
|
}
|
||||||
|
return NewCachedTokenProvider(&tokenProvider3LO{opts: opts, refreshToken: opts.RefreshToken, client: opts.client()}, &CachedTokenProviderOptions{
|
||||||
|
ExpireEarly: opts.EarlyTokenExpiry,
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthorizationHandlerOptions provides a set of options to specify for doing a
|
||||||
|
// 3-legged OAuth2 flow with a custom [AuthorizationHandler].
|
||||||
|
type AuthorizationHandlerOptions struct {
|
||||||
|
// AuthorizationHandler specifies the handler used to for the authorization
|
||||||
|
// part of the flow.
|
||||||
|
Handler AuthorizationHandler
|
||||||
|
// State is used verify that the "state" is identical in the request and
|
||||||
|
// response before exchanging the auth code for OAuth2 token.
|
||||||
|
State string
|
||||||
|
// PKCEOpts allows setting configurations for PKCE. Optional.
|
||||||
|
PKCEOpts *PKCEOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func new3LOTokenProviderWithAuthHandler(opts *Options3LO) TokenProvider {
|
||||||
|
return NewCachedTokenProvider(&tokenProviderWithHandler{opts: opts, state: opts.AuthHandlerOpts.State}, &CachedTokenProviderOptions{
|
||||||
|
ExpireEarly: opts.EarlyTokenExpiry,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// exchange handles the final exchange portion of the 3lo flow. Returns a Token,
|
||||||
|
// refreshToken, and error.
|
||||||
|
func (o *Options3LO) exchange(ctx context.Context, code string) (*Token, string, error) {
|
||||||
|
// Build request
|
||||||
|
v := url.Values{
|
||||||
|
"grant_type": {"authorization_code"},
|
||||||
|
"code": {code},
|
||||||
|
}
|
||||||
|
if o.RedirectURL != "" {
|
||||||
|
v.Set("redirect_uri", o.RedirectURL)
|
||||||
|
}
|
||||||
|
if o.AuthHandlerOpts != nil &&
|
||||||
|
o.AuthHandlerOpts.PKCEOpts != nil &&
|
||||||
|
o.AuthHandlerOpts.PKCEOpts.Verifier != "" {
|
||||||
|
v.Set(codeVerifierKey, o.AuthHandlerOpts.PKCEOpts.Verifier)
|
||||||
|
}
|
||||||
|
for k := range o.URLParams {
|
||||||
|
v.Set(k, o.URLParams.Get(k))
|
||||||
|
}
|
||||||
|
return fetchToken(ctx, o, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This struct is not safe for concurrent access alone, but the way it is used
|
||||||
|
// in this package by wrapping it with a cachedTokenProvider makes it so.
|
||||||
|
type tokenProvider3LO struct {
|
||||||
|
opts *Options3LO
|
||||||
|
client *http.Client
|
||||||
|
refreshToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tp *tokenProvider3LO) Token(ctx context.Context) (*Token, error) {
|
||||||
|
if tp.refreshToken == "" {
|
||||||
|
return nil, errors.New("auth: token expired and refresh token is not set")
|
||||||
|
}
|
||||||
|
v := url.Values{
|
||||||
|
"grant_type": {"refresh_token"},
|
||||||
|
"refresh_token": {tp.refreshToken},
|
||||||
|
}
|
||||||
|
for k := range tp.opts.URLParams {
|
||||||
|
v.Set(k, tp.opts.URLParams.Get(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
tk, rt, err := fetchToken(ctx, tp.opts, v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tp.refreshToken != rt && rt != "" {
|
||||||
|
tp.refreshToken = rt
|
||||||
|
}
|
||||||
|
return tk, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenProviderWithHandler struct {
|
||||||
|
opts *Options3LO
|
||||||
|
state string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tp tokenProviderWithHandler) Token(ctx context.Context) (*Token, error) {
|
||||||
|
url := tp.opts.authCodeURL(tp.state, nil)
|
||||||
|
code, state, err := tp.opts.AuthHandlerOpts.Handler(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if state != tp.state {
|
||||||
|
return nil, errors.New("auth: state mismatch in 3-legged-OAuth flow")
|
||||||
|
}
|
||||||
|
tok, _, err := tp.opts.exchange(ctx, code)
|
||||||
|
return tok, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchToken returns a Token, refresh token, and/or an error.
|
||||||
|
func fetchToken(ctx context.Context, o *Options3LO, v url.Values) (*Token, string, error) {
|
||||||
|
var refreshToken string
|
||||||
|
if o.AuthStyle == StyleInParams {
|
||||||
|
if o.ClientID != "" {
|
||||||
|
v.Set("client_id", o.ClientID)
|
||||||
|
}
|
||||||
|
if o.ClientSecret != "" {
|
||||||
|
v.Set("client_secret", o.ClientSecret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", o.TokenURL, strings.NewReader(v.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, refreshToken, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
if o.AuthStyle == StyleInHeader {
|
||||||
|
req.SetBasicAuth(url.QueryEscape(o.ClientID), url.QueryEscape(o.ClientSecret))
|
||||||
|
}
|
||||||
|
logger := o.logger()
|
||||||
|
|
||||||
|
logger.DebugContext(ctx, "3LO token request", "request", internallog.HTTPRequest(req, []byte(v.Encode())))
|
||||||
|
// Make request
|
||||||
|
resp, body, err := internal.DoRequest(o.client(), req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, refreshToken, err
|
||||||
|
}
|
||||||
|
logger.DebugContext(ctx, "3LO token response", "response", internallog.HTTPResponse(resp, body))
|
||||||
|
failureStatus := resp.StatusCode < 200 || resp.StatusCode > 299
|
||||||
|
tokError := &Error{
|
||||||
|
Response: resp,
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
|
|
||||||
|
var token *Token
|
||||||
|
// errors ignored because of default switch on content
|
||||||
|
content, _, _ := mime.ParseMediaType(resp.Header.Get("Content-Type"))
|
||||||
|
switch content {
|
||||||
|
case "application/x-www-form-urlencoded", "text/plain":
|
||||||
|
// some endpoints return a query string
|
||||||
|
vals, err := url.ParseQuery(string(body))
|
||||||
|
if err != nil {
|
||||||
|
if failureStatus {
|
||||||
|
return nil, refreshToken, tokError
|
||||||
|
}
|
||||||
|
return nil, refreshToken, fmt.Errorf("auth: cannot parse response: %w", err)
|
||||||
|
}
|
||||||
|
tokError.code = vals.Get("error")
|
||||||
|
tokError.description = vals.Get("error_description")
|
||||||
|
tokError.uri = vals.Get("error_uri")
|
||||||
|
token = &Token{
|
||||||
|
Value: vals.Get("access_token"),
|
||||||
|
Type: vals.Get("token_type"),
|
||||||
|
Metadata: make(map[string]interface{}, len(vals)),
|
||||||
|
}
|
||||||
|
for k, v := range vals {
|
||||||
|
token.Metadata[k] = v
|
||||||
|
}
|
||||||
|
refreshToken = vals.Get("refresh_token")
|
||||||
|
e := vals.Get("expires_in")
|
||||||
|
expires, _ := strconv.Atoi(e)
|
||||||
|
if expires != 0 {
|
||||||
|
token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
var tj tokenJSON
|
||||||
|
if err = json.Unmarshal(body, &tj); err != nil {
|
||||||
|
if failureStatus {
|
||||||
|
return nil, refreshToken, tokError
|
||||||
|
}
|
||||||
|
return nil, refreshToken, fmt.Errorf("auth: cannot parse json: %w", err)
|
||||||
|
}
|
||||||
|
tokError.code = tj.ErrorCode
|
||||||
|
tokError.description = tj.ErrorDescription
|
||||||
|
tokError.uri = tj.ErrorURI
|
||||||
|
token = &Token{
|
||||||
|
Value: tj.AccessToken,
|
||||||
|
Type: tj.TokenType,
|
||||||
|
Expiry: tj.expiry(),
|
||||||
|
Metadata: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
json.Unmarshal(body, &token.Metadata) // optional field, skip err check
|
||||||
|
refreshToken = tj.RefreshToken
|
||||||
|
}
|
||||||
|
// according to spec, servers should respond status 400 in error case
|
||||||
|
// https://www.rfc-editor.org/rfc/rfc6749#section-5.2
|
||||||
|
// but some unorthodox servers respond 200 in error case
|
||||||
|
if failureStatus || tokError.code != "" {
|
||||||
|
return nil, refreshToken, tokError
|
||||||
|
}
|
||||||
|
if token.Value == "" {
|
||||||
|
return nil, refreshToken, errors.New("auth: server response missing access_token")
|
||||||
|
}
|
||||||
|
return token, refreshToken, nil
|
||||||
|
}
|
||||||
115
vendor/cloud.google.com/go/compute/metadata/CHANGES.md
generated
vendored
Normal file
115
vendor/cloud.google.com/go/compute/metadata/CHANGES.md
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
# Changes
|
||||||
|
|
||||||
|
## [0.9.0](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.8.4...compute/metadata/v0.9.0) (2025-09-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compute/metadata:** Retry on HTTP 429 ([#12932](https://github.com/googleapis/google-cloud-go/issues/12932)) ([1e91f5c](https://github.com/googleapis/google-cloud-go/commit/1e91f5c07acacd38ecdd4ff3e83e092b745e0bc2))
|
||||||
|
|
||||||
|
## [0.8.4](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.8.3...compute/metadata/v0.8.4) (2025-09-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compute/metadata:** Set subClient for UseDefaultClient case ([#12911](https://github.com/googleapis/google-cloud-go/issues/12911)) ([9e2646b](https://github.com/googleapis/google-cloud-go/commit/9e2646b1821231183fd775bb107c062865eeaccd))
|
||||||
|
|
||||||
|
## [0.8.3](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.8.2...compute/metadata/v0.8.3) (2025-09-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compute/metadata:** Disable Client timeouts for subscription client ([#12910](https://github.com/googleapis/google-cloud-go/issues/12910)) ([187a58a](https://github.com/googleapis/google-cloud-go/commit/187a58a540494e1e8562b046325b8cad8cf7af4a))
|
||||||
|
|
||||||
|
## [0.8.2](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.8.1...compute/metadata/v0.8.2) (2025-09-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compute/metadata:** Racy test and uninitialized subClient ([#12892](https://github.com/googleapis/google-cloud-go/issues/12892)) ([4943ca2](https://github.com/googleapis/google-cloud-go/commit/4943ca2bf83908a23806247bc4252dfb440d09cc)), refs [#12888](https://github.com/googleapis/google-cloud-go/issues/12888)
|
||||||
|
|
||||||
|
## [0.8.1](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.8.0...compute/metadata/v0.8.1) (2025-09-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compute/metadata:** Use separate client for subscribe methods ([#12885](https://github.com/googleapis/google-cloud-go/issues/12885)) ([76b80f8](https://github.com/googleapis/google-cloud-go/commit/76b80f8df9bf9339d175407e8c15936fe1ac1c9c))
|
||||||
|
|
||||||
|
## [0.8.0](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.7.0...compute/metadata/v0.8.0) (2025-08-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compute/metadata:** Add Options.UseDefaultClient ([#12657](https://github.com/googleapis/google-cloud-go/issues/12657)) ([1a88209](https://github.com/googleapis/google-cloud-go/commit/1a8820900f20e038291c4bb2c5284a449196e81f)), refs [#11078](https://github.com/googleapis/google-cloud-go/issues/11078)
|
||||||
|
|
||||||
|
## [0.7.0](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.6.0...compute/metadata/v0.7.0) (2025-05-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compute/metadata:** Allow canceling GCE detection ([#11786](https://github.com/googleapis/google-cloud-go/issues/11786)) ([78100fe](https://github.com/googleapis/google-cloud-go/commit/78100fe7e28cd30f1e10b47191ac3c9839663b64))
|
||||||
|
|
||||||
|
## [0.6.0](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.5.2...compute/metadata/v0.6.0) (2024-12-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compute/metadata:** Add debug logging ([#11078](https://github.com/googleapis/google-cloud-go/issues/11078)) ([a816814](https://github.com/googleapis/google-cloud-go/commit/a81681463906e4473570a2f426eb0dc2de64e53f))
|
||||||
|
|
||||||
|
## [0.5.2](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.5.1...compute/metadata/v0.5.2) (2024-09-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compute/metadata:** Close Response Body for failed request ([#10891](https://github.com/googleapis/google-cloud-go/issues/10891)) ([e91d45e](https://github.com/googleapis/google-cloud-go/commit/e91d45e4757a9e354114509ba9800085d9e0ff1f))
|
||||||
|
|
||||||
|
## [0.5.1](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.5.0...compute/metadata/v0.5.1) (2024-09-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compute/metadata:** Check error chain for retryable error ([#10840](https://github.com/googleapis/google-cloud-go/issues/10840)) ([2bdedef](https://github.com/googleapis/google-cloud-go/commit/2bdedeff621b223d63cebc4355fcf83bc68412cd))
|
||||||
|
|
||||||
|
## [0.5.0](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.4.0...compute/metadata/v0.5.0) (2024-07-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compute/metadata:** Add sys check for windows OnGCE ([#10521](https://github.com/googleapis/google-cloud-go/issues/10521)) ([3b9a830](https://github.com/googleapis/google-cloud-go/commit/3b9a83063960d2a2ac20beb47cc15818a68bd302))
|
||||||
|
|
||||||
|
## [0.4.0](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.3.0...compute/metadata/v0.4.0) (2024-07-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compute/metadata:** Add context for all functions/methods ([#10370](https://github.com/googleapis/google-cloud-go/issues/10370)) ([66b8efe](https://github.com/googleapis/google-cloud-go/commit/66b8efe7ad877e052b2987bb4475477e38c67bb3))
|
||||||
|
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
* **compute/metadata:** Update OnGCE description ([#10408](https://github.com/googleapis/google-cloud-go/issues/10408)) ([6a46dca](https://github.com/googleapis/google-cloud-go/commit/6a46dca4eae4f88ec6f88822e01e5bf8aeca787f))
|
||||||
|
|
||||||
|
## [0.3.0](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.2.3...compute/metadata/v0.3.0) (2024-04-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compute/metadata:** Add context aware functions ([#9733](https://github.com/googleapis/google-cloud-go/issues/9733)) ([e4eb5b4](https://github.com/googleapis/google-cloud-go/commit/e4eb5b46ee2aec9d2fc18300bfd66015e25a0510))
|
||||||
|
|
||||||
|
## [0.2.3](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.2.2...compute/metadata/v0.2.3) (2022-12-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compute/metadata:** Switch DNS lookup to an absolute lookup ([119b410](https://github.com/googleapis/google-cloud-go/commit/119b41060c7895e45e48aee5621ad35607c4d021)), refs [#7165](https://github.com/googleapis/google-cloud-go/issues/7165)
|
||||||
|
|
||||||
|
## [0.2.2](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.2.1...compute/metadata/v0.2.2) (2022-12-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compute/metadata:** Set IdleConnTimeout for http.Client ([#7084](https://github.com/googleapis/google-cloud-go/issues/7084)) ([766516a](https://github.com/googleapis/google-cloud-go/commit/766516aaf3816bfb3159efeea65aa3d1d205a3e2)), refs [#5430](https://github.com/googleapis/google-cloud-go/issues/5430)
|
||||||
|
|
||||||
|
## [0.1.0] (2022-10-26)
|
||||||
|
|
||||||
|
Initial release of metadata being it's own module.
|
||||||
202
vendor/cloud.google.com/go/compute/metadata/LICENSE
generated
vendored
Normal file
202
vendor/cloud.google.com/go/compute/metadata/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
27
vendor/cloud.google.com/go/compute/metadata/README.md
generated
vendored
Normal file
27
vendor/cloud.google.com/go/compute/metadata/README.md
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Compute API
|
||||||
|
|
||||||
|
[](https://pkg.go.dev/cloud.google.com/go/compute/metadata)
|
||||||
|
|
||||||
|
This is a utility library for communicating with Google Cloud metadata service
|
||||||
|
on Google Cloud.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get cloud.google.com/go/compute/metadata
|
||||||
|
```
|
||||||
|
|
||||||
|
## Go Version Support
|
||||||
|
|
||||||
|
See the [Go Versions Supported](https://github.com/googleapis/google-cloud-go#go-versions-supported)
|
||||||
|
section in the root directory's README.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome. Please, see the [CONTRIBUTING](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/main/CONTRIBUTING.md)
|
||||||
|
document for details.
|
||||||
|
|
||||||
|
Please note that this project is released with a Contributor Code of Conduct.
|
||||||
|
By participating in this project you agree to abide by its terms. See
|
||||||
|
[Contributor Code of Conduct](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/main/CONTRIBUTING.md#contributor-code-of-conduct)
|
||||||
|
for more information.
|
||||||
149
vendor/cloud.google.com/go/compute/metadata/log.go
generated
vendored
Normal file
149
vendor/cloud.google.com/go/compute/metadata/log.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
// Copyright 2024 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Code below this point is copied from github.com/googleapis/gax-go/v2/internallog
|
||||||
|
// to avoid the dependency. The compute/metadata module is used by too many
|
||||||
|
// non-client library modules that can't justify the dependency.
|
||||||
|
|
||||||
|
// The handler returned if logging is not enabled.
|
||||||
|
type noOpHandler struct{}
|
||||||
|
|
||||||
|
func (h noOpHandler) Enabled(_ context.Context, _ slog.Level) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h noOpHandler) Handle(_ context.Context, _ slog.Record) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h noOpHandler) WithAttrs(_ []slog.Attr) slog.Handler {
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h noOpHandler) WithGroup(_ string) slog.Handler {
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpRequest returns a lazily evaluated [slog.LogValuer] for a
|
||||||
|
// [http.Request] and the associated body.
|
||||||
|
func httpRequest(req *http.Request, body []byte) slog.LogValuer {
|
||||||
|
return &request{
|
||||||
|
req: req,
|
||||||
|
payload: body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type request struct {
|
||||||
|
req *http.Request
|
||||||
|
payload []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *request) LogValue() slog.Value {
|
||||||
|
if r == nil || r.req == nil {
|
||||||
|
return slog.Value{}
|
||||||
|
}
|
||||||
|
var groupValueAttrs []slog.Attr
|
||||||
|
groupValueAttrs = append(groupValueAttrs, slog.String("method", r.req.Method))
|
||||||
|
groupValueAttrs = append(groupValueAttrs, slog.String("url", r.req.URL.String()))
|
||||||
|
|
||||||
|
var headerAttr []slog.Attr
|
||||||
|
for k, val := range r.req.Header {
|
||||||
|
headerAttr = append(headerAttr, slog.String(k, strings.Join(val, ",")))
|
||||||
|
}
|
||||||
|
if len(headerAttr) > 0 {
|
||||||
|
groupValueAttrs = append(groupValueAttrs, slog.Any("headers", headerAttr))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(r.payload) > 0 {
|
||||||
|
if attr, ok := processPayload(r.payload); ok {
|
||||||
|
groupValueAttrs = append(groupValueAttrs, attr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slog.GroupValue(groupValueAttrs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// httpResponse returns a lazily evaluated [slog.LogValuer] for a
|
||||||
|
// [http.Response] and the associated body.
|
||||||
|
func httpResponse(resp *http.Response, body []byte) slog.LogValuer {
|
||||||
|
return &response{
|
||||||
|
resp: resp,
|
||||||
|
payload: body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type response struct {
|
||||||
|
resp *http.Response
|
||||||
|
payload []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *response) LogValue() slog.Value {
|
||||||
|
if r == nil {
|
||||||
|
return slog.Value{}
|
||||||
|
}
|
||||||
|
var groupValueAttrs []slog.Attr
|
||||||
|
groupValueAttrs = append(groupValueAttrs, slog.String("status", fmt.Sprint(r.resp.StatusCode)))
|
||||||
|
|
||||||
|
var headerAttr []slog.Attr
|
||||||
|
for k, val := range r.resp.Header {
|
||||||
|
headerAttr = append(headerAttr, slog.String(k, strings.Join(val, ",")))
|
||||||
|
}
|
||||||
|
if len(headerAttr) > 0 {
|
||||||
|
groupValueAttrs = append(groupValueAttrs, slog.Any("headers", headerAttr))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(r.payload) > 0 {
|
||||||
|
if attr, ok := processPayload(r.payload); ok {
|
||||||
|
groupValueAttrs = append(groupValueAttrs, attr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return slog.GroupValue(groupValueAttrs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processPayload(payload []byte) (slog.Attr, bool) {
|
||||||
|
peekChar := payload[0]
|
||||||
|
if peekChar == '{' {
|
||||||
|
// JSON object
|
||||||
|
var m map[string]any
|
||||||
|
if err := json.Unmarshal(payload, &m); err == nil {
|
||||||
|
return slog.Any("payload", m), true
|
||||||
|
}
|
||||||
|
} else if peekChar == '[' {
|
||||||
|
// JSON array
|
||||||
|
var m []any
|
||||||
|
if err := json.Unmarshal(payload, &m); err == nil {
|
||||||
|
return slog.Any("payload", m), true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Everything else
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
if err := json.Compact(buf, payload); err != nil {
|
||||||
|
// Write raw payload incase of error
|
||||||
|
buf.Write(payload)
|
||||||
|
}
|
||||||
|
return slog.String("payload", buf.String()), true
|
||||||
|
}
|
||||||
|
return slog.Attr{}, false
|
||||||
|
}
|
||||||
937
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
Normal file
937
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
Normal file
@@ -0,0 +1,937 @@
|
|||||||
|
// Copyright 2014 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// Package metadata provides access to Google Compute Engine (GCE)
|
||||||
|
// metadata and API service accounts.
|
||||||
|
//
|
||||||
|
// This package is a wrapper around the GCE metadata service,
|
||||||
|
// as documented at https://cloud.google.com/compute/docs/metadata/overview.
|
||||||
|
package metadata // import "cloud.google.com/go/compute/metadata"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log/slog"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// metadataIP is the documented metadata server IP address.
|
||||||
|
metadataIP = "169.254.169.254"
|
||||||
|
|
||||||
|
// metadataHostEnv is the environment variable specifying the
|
||||||
|
// GCE metadata hostname. If empty, the default value of
|
||||||
|
// metadataIP ("169.254.169.254") is used instead.
|
||||||
|
// This is variable name is not defined by any spec, as far as
|
||||||
|
// I know; it was made up for the Go package.
|
||||||
|
metadataHostEnv = "GCE_METADATA_HOST"
|
||||||
|
|
||||||
|
userAgent = "gcloud-golang/0.1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type cachedValue struct {
|
||||||
|
k string
|
||||||
|
trim bool
|
||||||
|
mu sync.Mutex
|
||||||
|
v string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
projID = &cachedValue{k: "project/project-id", trim: true}
|
||||||
|
projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
|
||||||
|
instID = &cachedValue{k: "instance/id", trim: true}
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultClient = &Client{
|
||||||
|
hc: newDefaultHTTPClient(true),
|
||||||
|
subClient: newDefaultHTTPClient(false),
|
||||||
|
logger: slog.New(noOpHandler{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDefaultHTTPClient(enableTimeouts bool) *http.Client {
|
||||||
|
transport := &http.Transport{
|
||||||
|
Dial: (&net.Dialer{
|
||||||
|
Timeout: 2 * time.Second,
|
||||||
|
KeepAlive: 30 * time.Second,
|
||||||
|
}).Dial,
|
||||||
|
}
|
||||||
|
c := &http.Client{
|
||||||
|
Transport: transport,
|
||||||
|
}
|
||||||
|
if enableTimeouts {
|
||||||
|
transport.IdleConnTimeout = 60 * time.Second
|
||||||
|
c.Timeout = 5 * time.Second
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotDefinedError is returned when requested metadata is not defined.
|
||||||
|
//
|
||||||
|
// The underlying string is the suffix after "/computeMetadata/v1/".
|
||||||
|
//
|
||||||
|
// This error is not returned if the value is defined to be the empty
|
||||||
|
// string.
|
||||||
|
type NotDefinedError string
|
||||||
|
|
||||||
|
func (suffix NotDefinedError) Error() string {
|
||||||
|
return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cachedValue) get(ctx context.Context, cl *Client) (v string, err error) {
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
c.mu.Lock()
|
||||||
|
if c.v != "" {
|
||||||
|
return c.v, nil
|
||||||
|
}
|
||||||
|
if c.trim {
|
||||||
|
v, err = cl.getTrimmed(ctx, c.k)
|
||||||
|
} else {
|
||||||
|
v, err = cl.GetWithContext(ctx, c.k)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
c.v = v
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
onGCEOnce sync.Once
|
||||||
|
onGCE bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// OnGCE reports whether this process is running on Google Compute Platforms.
|
||||||
|
// NOTE: True returned from `OnGCE` does not guarantee that the metadata server
|
||||||
|
// is accessible from this process and have all the metadata defined.
|
||||||
|
func OnGCE() bool {
|
||||||
|
return OnGCEWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnGCEWithContext reports whether this process is running on Google Compute Platforms.
|
||||||
|
// This function's return value is memoized for better performance.
|
||||||
|
// NOTE: True returned from `OnGCEWithContext` does not guarantee that the metadata server
|
||||||
|
// is accessible from this process and have all the metadata defined.
|
||||||
|
func OnGCEWithContext(ctx context.Context) bool {
|
||||||
|
onGCEOnce.Do(func() {
|
||||||
|
onGCE = defaultClient.OnGCEWithContext(ctx)
|
||||||
|
})
|
||||||
|
return onGCE
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe calls Client.SubscribeWithContext on the default client.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [SubscribeWithContext].
|
||||||
|
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
|
||||||
|
return defaultClient.SubscribeWithContext(context.Background(), suffix, func(ctx context.Context, v string, ok bool) error { return fn(v, ok) })
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeWithContext calls Client.SubscribeWithContext on the default client.
|
||||||
|
func SubscribeWithContext(ctx context.Context, suffix string, fn func(ctx context.Context, v string, ok bool) error) error {
|
||||||
|
return defaultClient.SubscribeWithContext(ctx, suffix, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get calls Client.GetWithContext on the default client.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [GetWithContext].
|
||||||
|
func Get(suffix string) (string, error) {
|
||||||
|
return defaultClient.GetWithContext(context.Background(), suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWithContext calls Client.GetWithContext on the default client.
|
||||||
|
func GetWithContext(ctx context.Context, suffix string) (string, error) {
|
||||||
|
return defaultClient.GetWithContext(ctx, suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectID returns the current instance's project ID string.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [ProjectIDWithContext].
|
||||||
|
func ProjectID() (string, error) {
|
||||||
|
return defaultClient.ProjectIDWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectIDWithContext returns the current instance's project ID string.
|
||||||
|
func ProjectIDWithContext(ctx context.Context) (string, error) {
|
||||||
|
return defaultClient.ProjectIDWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumericProjectID returns the current instance's numeric project ID.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [NumericProjectIDWithContext].
|
||||||
|
func NumericProjectID() (string, error) {
|
||||||
|
return defaultClient.NumericProjectIDWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumericProjectIDWithContext returns the current instance's numeric project ID.
|
||||||
|
func NumericProjectIDWithContext(ctx context.Context) (string, error) {
|
||||||
|
return defaultClient.NumericProjectIDWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalIP returns the instance's primary internal IP address.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [InternalIPWithContext].
|
||||||
|
func InternalIP() (string, error) {
|
||||||
|
return defaultClient.InternalIPWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalIPWithContext returns the instance's primary internal IP address.
|
||||||
|
func InternalIPWithContext(ctx context.Context) (string, error) {
|
||||||
|
return defaultClient.InternalIPWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalIP returns the instance's primary external (public) IP address.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [ExternalIPWithContext].
|
||||||
|
func ExternalIP() (string, error) {
|
||||||
|
return defaultClient.ExternalIPWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalIPWithContext returns the instance's primary external (public) IP address.
|
||||||
|
func ExternalIPWithContext(ctx context.Context) (string, error) {
|
||||||
|
return defaultClient.ExternalIPWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email calls Client.EmailWithContext on the default client.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [EmailWithContext].
|
||||||
|
func Email(serviceAccount string) (string, error) {
|
||||||
|
return defaultClient.EmailWithContext(context.Background(), serviceAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmailWithContext calls Client.EmailWithContext on the default client.
|
||||||
|
func EmailWithContext(ctx context.Context, serviceAccount string) (string, error) {
|
||||||
|
return defaultClient.EmailWithContext(ctx, serviceAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hostname returns the instance's hostname. This will be of the form
|
||||||
|
// "<instanceID>.c.<projID>.internal".
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [HostnameWithContext].
|
||||||
|
func Hostname() (string, error) {
|
||||||
|
return defaultClient.HostnameWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostnameWithContext returns the instance's hostname. This will be of the form
|
||||||
|
// "<instanceID>.c.<projID>.internal".
|
||||||
|
func HostnameWithContext(ctx context.Context) (string, error) {
|
||||||
|
return defaultClient.HostnameWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceTags returns the list of user-defined instance tags,
|
||||||
|
// assigned when initially creating a GCE instance.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [InstanceTagsWithContext].
|
||||||
|
func InstanceTags() ([]string, error) {
|
||||||
|
return defaultClient.InstanceTagsWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceTagsWithContext returns the list of user-defined instance tags,
|
||||||
|
// assigned when initially creating a GCE instance.
|
||||||
|
func InstanceTagsWithContext(ctx context.Context) ([]string, error) {
|
||||||
|
return defaultClient.InstanceTagsWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceID returns the current VM's numeric instance ID.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [InstanceIDWithContext].
|
||||||
|
func InstanceID() (string, error) {
|
||||||
|
return defaultClient.InstanceIDWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceIDWithContext returns the current VM's numeric instance ID.
|
||||||
|
func InstanceIDWithContext(ctx context.Context) (string, error) {
|
||||||
|
return defaultClient.InstanceIDWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceName returns the current VM's instance ID string.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [InstanceNameWithContext].
|
||||||
|
func InstanceName() (string, error) {
|
||||||
|
return defaultClient.InstanceNameWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceNameWithContext returns the current VM's instance ID string.
|
||||||
|
func InstanceNameWithContext(ctx context.Context) (string, error) {
|
||||||
|
return defaultClient.InstanceNameWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zone returns the current VM's zone, such as "us-central1-b".
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [ZoneWithContext].
|
||||||
|
func Zone() (string, error) {
|
||||||
|
return defaultClient.ZoneWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneWithContext returns the current VM's zone, such as "us-central1-b".
|
||||||
|
func ZoneWithContext(ctx context.Context) (string, error) {
|
||||||
|
return defaultClient.ZoneWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceAttributes calls Client.InstanceAttributesWithContext on the default client.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [InstanceAttributesWithContext.
|
||||||
|
func InstanceAttributes() ([]string, error) {
|
||||||
|
return defaultClient.InstanceAttributesWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceAttributesWithContext calls Client.ProjectAttributesWithContext on the default client.
|
||||||
|
func InstanceAttributesWithContext(ctx context.Context) ([]string, error) {
|
||||||
|
return defaultClient.InstanceAttributesWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectAttributes calls Client.ProjectAttributesWithContext on the default client.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [ProjectAttributesWithContext].
|
||||||
|
func ProjectAttributes() ([]string, error) {
|
||||||
|
return defaultClient.ProjectAttributesWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectAttributesWithContext calls Client.ProjectAttributesWithContext on the default client.
|
||||||
|
func ProjectAttributesWithContext(ctx context.Context) ([]string, error) {
|
||||||
|
return defaultClient.ProjectAttributesWithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceAttributeValue calls Client.InstanceAttributeValueWithContext on the default client.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [InstanceAttributeValueWithContext].
|
||||||
|
func InstanceAttributeValue(attr string) (string, error) {
|
||||||
|
return defaultClient.InstanceAttributeValueWithContext(context.Background(), attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceAttributeValueWithContext calls Client.InstanceAttributeValueWithContext on the default client.
|
||||||
|
func InstanceAttributeValueWithContext(ctx context.Context, attr string) (string, error) {
|
||||||
|
return defaultClient.InstanceAttributeValueWithContext(ctx, attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectAttributeValue calls Client.ProjectAttributeValueWithContext on the default client.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [ProjectAttributeValueWithContext].
|
||||||
|
func ProjectAttributeValue(attr string) (string, error) {
|
||||||
|
return defaultClient.ProjectAttributeValueWithContext(context.Background(), attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectAttributeValueWithContext calls Client.ProjectAttributeValueWithContext on the default client.
|
||||||
|
func ProjectAttributeValueWithContext(ctx context.Context, attr string) (string, error) {
|
||||||
|
return defaultClient.ProjectAttributeValueWithContext(ctx, attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scopes calls Client.ScopesWithContext on the default client.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [ScopesWithContext].
|
||||||
|
func Scopes(serviceAccount string) ([]string, error) {
|
||||||
|
return defaultClient.ScopesWithContext(context.Background(), serviceAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScopesWithContext calls Client.ScopesWithContext on the default client.
|
||||||
|
func ScopesWithContext(ctx context.Context, serviceAccount string) ([]string, error) {
|
||||||
|
return defaultClient.ScopesWithContext(ctx, serviceAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func strsContains(ss []string, s string) bool {
|
||||||
|
for _, v := range ss {
|
||||||
|
if v == s {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Client provides metadata.
|
||||||
|
type Client struct {
|
||||||
|
hc *http.Client
|
||||||
|
// subClient by default is a HTTP Client that is only used for subscribe
|
||||||
|
// methods that should not specify a timeout. If the user specifies a client
|
||||||
|
// this with be the same as 'hc'.
|
||||||
|
subClient *http.Client
|
||||||
|
logger *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options for configuring a [Client].
|
||||||
|
type Options struct {
|
||||||
|
// Client is the HTTP client used to make requests. Optional.
|
||||||
|
// If UseDefaultClient is true, this field is ignored.
|
||||||
|
// If this field is nil, a new default http.Client will be created.
|
||||||
|
Client *http.Client
|
||||||
|
// Logger is used to log information about HTTP request and responses.
|
||||||
|
// If not provided, nothing will be logged. Optional.
|
||||||
|
Logger *slog.Logger
|
||||||
|
// UseDefaultClient specifies that the client should use the same default
|
||||||
|
// internal http.Client that is used in functions such as GetWithContext.
|
||||||
|
// This is useful for sharing a single TCP connection pool across requests.
|
||||||
|
// The difference vs GetWithContext is the ability to use this struct
|
||||||
|
// to provide a custom logger. If this field is true, the Client
|
||||||
|
// field is ignored.
|
||||||
|
UseDefaultClient bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a Client that can be used to fetch metadata.
|
||||||
|
// Returns the client that uses the specified http.Client for HTTP requests.
|
||||||
|
// If nil is specified, returns the default internal Client that is
|
||||||
|
// also used in functions such as GetWithContext. This is useful for sharing
|
||||||
|
// a single TCP connection pool across requests.
|
||||||
|
func NewClient(c *http.Client) *Client {
|
||||||
|
if c == nil {
|
||||||
|
// Preserve original behavior for nil argument.
|
||||||
|
return defaultClient
|
||||||
|
}
|
||||||
|
// Return a new client with a no-op logger for backward compatibility.
|
||||||
|
return &Client{hc: c, subClient: c, logger: slog.New(noOpHandler{})}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWithOptions returns a Client that is configured with the provided Options.
|
||||||
|
func NewWithOptions(opts *Options) *Client {
|
||||||
|
// Preserve original behavior for nil opts.
|
||||||
|
if opts == nil {
|
||||||
|
return defaultClient
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle explicit request for the internal default http.Client.
|
||||||
|
if opts.UseDefaultClient {
|
||||||
|
logger := opts.Logger
|
||||||
|
if logger == nil {
|
||||||
|
logger = slog.New(noOpHandler{})
|
||||||
|
}
|
||||||
|
return &Client{hc: defaultClient.hc, subClient: defaultClient.subClient, logger: logger}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle isolated client creation.
|
||||||
|
client := opts.Client
|
||||||
|
subClient := opts.Client
|
||||||
|
if client == nil {
|
||||||
|
client = newDefaultHTTPClient(true)
|
||||||
|
subClient = newDefaultHTTPClient(false)
|
||||||
|
}
|
||||||
|
logger := opts.Logger
|
||||||
|
if logger == nil {
|
||||||
|
logger = slog.New(noOpHandler{})
|
||||||
|
}
|
||||||
|
return &Client{hc: client, subClient: subClient, logger: logger}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: metadataRequestStrategy is assigned to a variable for test stubbing purposes.
|
||||||
|
var metadataRequestStrategy = func(ctx context.Context, httpClient *http.Client, resc chan bool) {
|
||||||
|
req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
|
||||||
|
req.Header.Set("User-Agent", userAgent)
|
||||||
|
res, err := httpClient.Do(req.WithContext(ctx))
|
||||||
|
if err != nil {
|
||||||
|
resc <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
resc <- res.Header.Get("Metadata-Flavor") == "Google"
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: dnsRequestStrategy is assigned to a variable for test stubbing purposes.
|
||||||
|
var dnsRequestStrategy = func(ctx context.Context, resc chan bool) {
|
||||||
|
resolver := &net.Resolver{}
|
||||||
|
addrs, err := resolver.LookupHost(ctx, "metadata.google.internal.")
|
||||||
|
if err != nil || len(addrs) == 0 {
|
||||||
|
resc <- false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resc <- strsContains(addrs, metadataIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnGCEWithContext reports whether this process is running on Google Compute Platforms.
|
||||||
|
// NOTE: True returned from `OnGCEWithContext` does not guarantee that the metadata server
|
||||||
|
// is accessible from this process and have all the metadata defined.
|
||||||
|
func (c *Client) OnGCEWithContext(ctx context.Context) bool {
|
||||||
|
// The user explicitly said they're on GCE, so trust them.
|
||||||
|
if os.Getenv(metadataHostEnv) != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
resc := make(chan bool, 2)
|
||||||
|
|
||||||
|
// Try two strategies in parallel.
|
||||||
|
// See https://github.com/googleapis/google-cloud-go/issues/194
|
||||||
|
go metadataRequestStrategy(ctx, c.hc, resc)
|
||||||
|
go dnsRequestStrategy(ctx, resc)
|
||||||
|
|
||||||
|
tryHarder := systemInfoSuggestsGCE()
|
||||||
|
if tryHarder {
|
||||||
|
res := <-resc
|
||||||
|
if res {
|
||||||
|
// The first strategy succeeded, so let's use it.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for either the DNS or metadata server probe to
|
||||||
|
// contradict the other one and say we are running on
|
||||||
|
// GCE. Give it a lot of time to do so, since the system
|
||||||
|
// info already suggests we're running on a GCE BIOS.
|
||||||
|
// Ensure cancellations from the calling context are respected.
|
||||||
|
waitContext, cancelWait := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer cancelWait()
|
||||||
|
select {
|
||||||
|
case res = <-resc:
|
||||||
|
return res
|
||||||
|
case <-waitContext.Done():
|
||||||
|
// Too slow. Who knows what this system is.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's no hint from the system info that we're running on
|
||||||
|
// GCE, so use the first probe's result as truth, whether it's
|
||||||
|
// true or false. The goal here is to optimize for speed for
|
||||||
|
// users who are NOT running on GCE. We can't assume that
|
||||||
|
// either a DNS lookup or an HTTP request to a blackholed IP
|
||||||
|
// address is fast. Worst case this should return when the
|
||||||
|
// metaClient's Transport.ResponseHeaderTimeout or
|
||||||
|
// Transport.Dial.Timeout fires (in two seconds).
|
||||||
|
return <-resc
|
||||||
|
}
|
||||||
|
|
||||||
|
// getETag returns a value from the metadata service as well as the associated ETag.
|
||||||
|
// This func is otherwise equivalent to Get.
|
||||||
|
func (c *Client) getETag(ctx context.Context, suffix string) (value, etag string, err error) {
|
||||||
|
return c.getETagWithSubClient(ctx, suffix, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getETagWithSubClient(ctx context.Context, suffix string, enableSubClient bool) (value, etag string, err error) {
|
||||||
|
// Using a fixed IP makes it very difficult to spoof the metadata service in
|
||||||
|
// a container, which is an important use-case for local testing of cloud
|
||||||
|
// deployments. To enable spoofing of the metadata service, the environment
|
||||||
|
// variable GCE_METADATA_HOST is first inspected to decide where metadata
|
||||||
|
// requests shall go.
|
||||||
|
host := os.Getenv(metadataHostEnv)
|
||||||
|
if host == "" {
|
||||||
|
// Using 169.254.169.254 instead of "metadata" here because Go
|
||||||
|
// binaries built with the "netgo" tag and without cgo won't
|
||||||
|
// know the search suffix for "metadata" is
|
||||||
|
// ".google.internal", and this IP address is documented as
|
||||||
|
// being stable anyway.
|
||||||
|
host = metadataIP
|
||||||
|
}
|
||||||
|
suffix = strings.TrimLeft(suffix, "/")
|
||||||
|
u := "http://" + host + "/computeMetadata/v1/" + suffix
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", u, nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
req.Header.Set("Metadata-Flavor", "Google")
|
||||||
|
req.Header.Set("User-Agent", userAgent)
|
||||||
|
var res *http.Response
|
||||||
|
var reqErr error
|
||||||
|
var body []byte
|
||||||
|
retryer := newRetryer()
|
||||||
|
hc := c.hc
|
||||||
|
if enableSubClient {
|
||||||
|
hc = c.subClient
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
c.logger.DebugContext(ctx, "metadata request", "request", httpRequest(req, nil))
|
||||||
|
res, reqErr = hc.Do(req)
|
||||||
|
var code int
|
||||||
|
if res != nil {
|
||||||
|
code = res.StatusCode
|
||||||
|
body, err = io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
res.Body.Close()
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
c.logger.DebugContext(ctx, "metadata response", "response", httpResponse(res, body))
|
||||||
|
res.Body.Close()
|
||||||
|
}
|
||||||
|
if delay, shouldRetry := retryer.Retry(code, reqErr); shouldRetry {
|
||||||
|
if res != nil && res.Body != nil {
|
||||||
|
res.Body.Close()
|
||||||
|
}
|
||||||
|
if err := sleep(ctx, delay); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if reqErr != nil {
|
||||||
|
return "", "", reqErr
|
||||||
|
}
|
||||||
|
if res.StatusCode == http.StatusNotFound {
|
||||||
|
return "", "", NotDefinedError(suffix)
|
||||||
|
}
|
||||||
|
if res.StatusCode != 200 {
|
||||||
|
return "", "", &Error{Code: res.StatusCode, Message: string(body)}
|
||||||
|
}
|
||||||
|
return string(body), res.Header.Get("Etag"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a value from the metadata service.
|
||||||
|
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
||||||
|
//
|
||||||
|
// If the GCE_METADATA_HOST environment variable is not defined, a default of
|
||||||
|
// 169.254.169.254 will be used instead.
|
||||||
|
//
|
||||||
|
// If the requested metadata is not defined, the returned error will
|
||||||
|
// be of type NotDefinedError.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.GetWithContext].
|
||||||
|
func (c *Client) Get(suffix string) (string, error) {
|
||||||
|
return c.GetWithContext(context.Background(), suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWithContext returns a value from the metadata service.
|
||||||
|
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
||||||
|
//
|
||||||
|
// If the GCE_METADATA_HOST environment variable is not defined, a default of
|
||||||
|
// 169.254.169.254 will be used instead.
|
||||||
|
//
|
||||||
|
// If the requested metadata is not defined, the returned error will
|
||||||
|
// be of type NotDefinedError.
|
||||||
|
//
|
||||||
|
// NOTE: Without an extra deadline in the context this call can take in the
|
||||||
|
// worst case, with internal backoff retries, up to 15 seconds (e.g. when server
|
||||||
|
// is responding slowly). Pass context with additional timeouts when needed.
|
||||||
|
func (c *Client) GetWithContext(ctx context.Context, suffix string) (string, error) {
|
||||||
|
val, _, err := c.getETag(ctx, suffix)
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) getTrimmed(ctx context.Context, suffix string) (s string, err error) {
|
||||||
|
s, err = c.GetWithContext(ctx, suffix)
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) lines(ctx context.Context, suffix string) ([]string, error) {
|
||||||
|
j, err := c.GetWithContext(ctx, suffix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s := strings.Split(strings.TrimSpace(j), "\n")
|
||||||
|
for i := range s {
|
||||||
|
s[i] = strings.TrimSpace(s[i])
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectID returns the current instance's project ID string.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.ProjectIDWithContext].
|
||||||
|
func (c *Client) ProjectID() (string, error) { return c.ProjectIDWithContext(context.Background()) }
|
||||||
|
|
||||||
|
// ProjectIDWithContext returns the current instance's project ID string.
|
||||||
|
func (c *Client) ProjectIDWithContext(ctx context.Context) (string, error) { return projID.get(ctx, c) }
|
||||||
|
|
||||||
|
// NumericProjectID returns the current instance's numeric project ID.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.NumericProjectIDWithContext].
|
||||||
|
func (c *Client) NumericProjectID() (string, error) {
|
||||||
|
return c.NumericProjectIDWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NumericProjectIDWithContext returns the current instance's numeric project ID.
|
||||||
|
func (c *Client) NumericProjectIDWithContext(ctx context.Context) (string, error) {
|
||||||
|
return projNum.get(ctx, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceID returns the current VM's numeric instance ID.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.InstanceIDWithContext].
|
||||||
|
func (c *Client) InstanceID() (string, error) {
|
||||||
|
return c.InstanceIDWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceIDWithContext returns the current VM's numeric instance ID.
|
||||||
|
func (c *Client) InstanceIDWithContext(ctx context.Context) (string, error) {
|
||||||
|
return instID.get(ctx, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalIP returns the instance's primary internal IP address.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.InternalIPWithContext].
|
||||||
|
func (c *Client) InternalIP() (string, error) {
|
||||||
|
return c.InternalIPWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InternalIPWithContext returns the instance's primary internal IP address.
|
||||||
|
func (c *Client) InternalIPWithContext(ctx context.Context) (string, error) {
|
||||||
|
return c.getTrimmed(ctx, "instance/network-interfaces/0/ip")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email returns the email address associated with the service account.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.EmailWithContext].
|
||||||
|
func (c *Client) Email(serviceAccount string) (string, error) {
|
||||||
|
return c.EmailWithContext(context.Background(), serviceAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmailWithContext returns the email address associated with the service account.
|
||||||
|
// The serviceAccount parameter default value (empty string or "default" value)
|
||||||
|
// will use the instance's main account.
|
||||||
|
func (c *Client) EmailWithContext(ctx context.Context, serviceAccount string) (string, error) {
|
||||||
|
if serviceAccount == "" {
|
||||||
|
serviceAccount = "default"
|
||||||
|
}
|
||||||
|
return c.getTrimmed(ctx, "instance/service-accounts/"+serviceAccount+"/email")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalIP returns the instance's primary external (public) IP address.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.ExternalIPWithContext].
|
||||||
|
func (c *Client) ExternalIP() (string, error) {
|
||||||
|
return c.ExternalIPWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExternalIPWithContext returns the instance's primary external (public) IP address.
|
||||||
|
func (c *Client) ExternalIPWithContext(ctx context.Context) (string, error) {
|
||||||
|
return c.getTrimmed(ctx, "instance/network-interfaces/0/access-configs/0/external-ip")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hostname returns the instance's hostname. This will be of the form
|
||||||
|
// "<instanceID>.c.<projID>.internal".
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.HostnameWithContext].
|
||||||
|
func (c *Client) Hostname() (string, error) {
|
||||||
|
return c.HostnameWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// HostnameWithContext returns the instance's hostname. This will be of the form
|
||||||
|
// "<instanceID>.c.<projID>.internal".
|
||||||
|
func (c *Client) HostnameWithContext(ctx context.Context) (string, error) {
|
||||||
|
return c.getTrimmed(ctx, "instance/hostname")
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceTags returns the list of user-defined instance tags.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.InstanceTagsWithContext].
|
||||||
|
func (c *Client) InstanceTags() ([]string, error) {
|
||||||
|
return c.InstanceTagsWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceTagsWithContext returns the list of user-defined instance tags,
|
||||||
|
// assigned when initially creating a GCE instance.
|
||||||
|
func (c *Client) InstanceTagsWithContext(ctx context.Context) ([]string, error) {
|
||||||
|
var s []string
|
||||||
|
j, err := c.GetWithContext(ctx, "instance/tags")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceName returns the current VM's instance ID string.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.InstanceNameWithContext].
|
||||||
|
func (c *Client) InstanceName() (string, error) {
|
||||||
|
return c.InstanceNameWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceNameWithContext returns the current VM's instance ID string.
|
||||||
|
func (c *Client) InstanceNameWithContext(ctx context.Context) (string, error) {
|
||||||
|
return c.getTrimmed(ctx, "instance/name")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zone returns the current VM's zone, such as "us-central1-b".
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.ZoneWithContext].
|
||||||
|
func (c *Client) Zone() (string, error) {
|
||||||
|
return c.ZoneWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZoneWithContext returns the current VM's zone, such as "us-central1-b".
|
||||||
|
func (c *Client) ZoneWithContext(ctx context.Context) (string, error) {
|
||||||
|
zone, err := c.getTrimmed(ctx, "instance/zone")
|
||||||
|
// zone is of the form "projects/<projNum>/zones/<zoneName>".
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return zone[strings.LastIndex(zone, "/")+1:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceAttributes returns the list of user-defined attributes,
|
||||||
|
// assigned when initially creating a GCE VM instance. The value of an
|
||||||
|
// attribute can be obtained with InstanceAttributeValue.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.InstanceAttributesWithContext].
|
||||||
|
func (c *Client) InstanceAttributes() ([]string, error) {
|
||||||
|
return c.InstanceAttributesWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceAttributesWithContext returns the list of user-defined attributes,
|
||||||
|
// assigned when initially creating a GCE VM instance. The value of an
|
||||||
|
// attribute can be obtained with InstanceAttributeValue.
|
||||||
|
func (c *Client) InstanceAttributesWithContext(ctx context.Context) ([]string, error) {
|
||||||
|
return c.lines(ctx, "instance/attributes/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectAttributes returns the list of user-defined attributes
|
||||||
|
// applying to the project as a whole, not just this VM. The value of
|
||||||
|
// an attribute can be obtained with ProjectAttributeValue.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.ProjectAttributesWithContext].
|
||||||
|
func (c *Client) ProjectAttributes() ([]string, error) {
|
||||||
|
return c.ProjectAttributesWithContext(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectAttributesWithContext returns the list of user-defined attributes
|
||||||
|
// applying to the project as a whole, not just this VM. The value of
|
||||||
|
// an attribute can be obtained with ProjectAttributeValue.
|
||||||
|
func (c *Client) ProjectAttributesWithContext(ctx context.Context) ([]string, error) {
|
||||||
|
return c.lines(ctx, "project/attributes/")
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceAttributeValue returns the value of the provided VM
|
||||||
|
// instance attribute.
|
||||||
|
//
|
||||||
|
// If the requested attribute is not defined, the returned error will
|
||||||
|
// be of type NotDefinedError.
|
||||||
|
//
|
||||||
|
// InstanceAttributeValue may return ("", nil) if the attribute was
|
||||||
|
// defined to be the empty string.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.InstanceAttributeValueWithContext].
|
||||||
|
func (c *Client) InstanceAttributeValue(attr string) (string, error) {
|
||||||
|
return c.InstanceAttributeValueWithContext(context.Background(), attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InstanceAttributeValueWithContext returns the value of the provided VM
|
||||||
|
// instance attribute.
|
||||||
|
//
|
||||||
|
// If the requested attribute is not defined, the returned error will
|
||||||
|
// be of type NotDefinedError.
|
||||||
|
//
|
||||||
|
// InstanceAttributeValue may return ("", nil) if the attribute was
|
||||||
|
// defined to be the empty string.
|
||||||
|
func (c *Client) InstanceAttributeValueWithContext(ctx context.Context, attr string) (string, error) {
|
||||||
|
return c.GetWithContext(ctx, "instance/attributes/"+attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectAttributeValue returns the value of the provided
|
||||||
|
// project attribute.
|
||||||
|
//
|
||||||
|
// If the requested attribute is not defined, the returned error will
|
||||||
|
// be of type NotDefinedError.
|
||||||
|
//
|
||||||
|
// ProjectAttributeValue may return ("", nil) if the attribute was
|
||||||
|
// defined to be the empty string.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.ProjectAttributeValueWithContext].
|
||||||
|
func (c *Client) ProjectAttributeValue(attr string) (string, error) {
|
||||||
|
return c.ProjectAttributeValueWithContext(context.Background(), attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectAttributeValueWithContext returns the value of the provided
|
||||||
|
// project attribute.
|
||||||
|
//
|
||||||
|
// If the requested attribute is not defined, the returned error will
|
||||||
|
// be of type NotDefinedError.
|
||||||
|
//
|
||||||
|
// ProjectAttributeValue may return ("", nil) if the attribute was
|
||||||
|
// defined to be the empty string.
|
||||||
|
func (c *Client) ProjectAttributeValueWithContext(ctx context.Context, attr string) (string, error) {
|
||||||
|
return c.GetWithContext(ctx, "project/attributes/"+attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scopes returns the service account scopes for the given account.
|
||||||
|
// The account may be empty or the string "default" to use the instance's
|
||||||
|
// main account.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.ScopesWithContext].
|
||||||
|
func (c *Client) Scopes(serviceAccount string) ([]string, error) {
|
||||||
|
return c.ScopesWithContext(context.Background(), serviceAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScopesWithContext returns the service account scopes for the given account.
|
||||||
|
// The account may be empty or the string "default" to use the instance's
|
||||||
|
// main account.
|
||||||
|
func (c *Client) ScopesWithContext(ctx context.Context, serviceAccount string) ([]string, error) {
|
||||||
|
if serviceAccount == "" {
|
||||||
|
serviceAccount = "default"
|
||||||
|
}
|
||||||
|
return c.lines(ctx, "instance/service-accounts/"+serviceAccount+"/scopes")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribe subscribes to a value from the metadata service.
|
||||||
|
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
||||||
|
// The suffix may contain query parameters.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use the context aware variant [Client.SubscribeWithContext].
|
||||||
|
func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error {
|
||||||
|
return c.SubscribeWithContext(context.Background(), suffix, func(ctx context.Context, v string, ok bool) error { return fn(v, ok) })
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeWithContext subscribes to a value from the metadata service.
|
||||||
|
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
||||||
|
// The suffix may contain query parameters.
|
||||||
|
//
|
||||||
|
// SubscribeWithContext calls fn with the latest metadata value indicated by the
|
||||||
|
// provided suffix. If the metadata value is deleted, fn is called with the
|
||||||
|
// empty string and ok false. Subscribe blocks until fn returns a non-nil error
|
||||||
|
// or the value is deleted. Subscribe returns the error value returned from the
|
||||||
|
// last call to fn, which may be nil when ok == false.
|
||||||
|
func (c *Client) SubscribeWithContext(ctx context.Context, suffix string, fn func(ctx context.Context, v string, ok bool) error) error {
|
||||||
|
const failedSubscribeSleep = time.Second * 5
|
||||||
|
|
||||||
|
// First check to see if the metadata value exists at all.
|
||||||
|
val, lastETag, err := c.getETagWithSubClient(ctx, suffix, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fn(ctx, val, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := true
|
||||||
|
if strings.ContainsRune(suffix, '?') {
|
||||||
|
suffix += "&wait_for_change=true&last_etag="
|
||||||
|
} else {
|
||||||
|
suffix += "?wait_for_change=true&last_etag="
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
val, etag, err := c.getETagWithSubClient(ctx, suffix+url.QueryEscape(lastETag), true)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, deleted := err.(NotDefinedError); !deleted {
|
||||||
|
time.Sleep(failedSubscribeSleep)
|
||||||
|
continue // Retry on other errors.
|
||||||
|
}
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
lastETag = etag
|
||||||
|
|
||||||
|
if err := fn(ctx, val, ok); err != nil || !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error contains an error response from the server.
|
||||||
|
type Error struct {
|
||||||
|
// Code is the HTTP response status code.
|
||||||
|
Code int
|
||||||
|
// Message is the server response message.
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
return fmt.Sprintf("compute: Received %d `%s`", e.Code, e.Message)
|
||||||
|
}
|
||||||
117
vendor/cloud.google.com/go/compute/metadata/retry.go
generated
vendored
Normal file
117
vendor/cloud.google.com/go/compute/metadata/retry.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
// Copyright 2021 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxRetryAttempts = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
syscallRetryable = func(error) bool { return false }
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultBackoff is basically equivalent to gax.Backoff without the need for
|
||||||
|
// the dependency.
|
||||||
|
type defaultBackoff struct {
|
||||||
|
max time.Duration
|
||||||
|
mul float64
|
||||||
|
cur time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *defaultBackoff) Pause() time.Duration {
|
||||||
|
d := time.Duration(1 + rand.Int63n(int64(b.cur)))
|
||||||
|
b.cur = time.Duration(float64(b.cur) * b.mul)
|
||||||
|
if b.cur > b.max {
|
||||||
|
b.cur = b.max
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// sleep is the equivalent of gax.Sleep without the need for the dependency.
|
||||||
|
func sleep(ctx context.Context, d time.Duration) error {
|
||||||
|
t := time.NewTimer(d)
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Stop()
|
||||||
|
return ctx.Err()
|
||||||
|
case <-t.C:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRetryer() *metadataRetryer {
|
||||||
|
return &metadataRetryer{bo: &defaultBackoff{
|
||||||
|
cur: 100 * time.Millisecond,
|
||||||
|
max: 30 * time.Second,
|
||||||
|
mul: 2,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type backoff interface {
|
||||||
|
Pause() time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type metadataRetryer struct {
|
||||||
|
bo backoff
|
||||||
|
attempts int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *metadataRetryer) Retry(status int, err error) (time.Duration, bool) {
|
||||||
|
if status == http.StatusOK {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
retryOk := shouldRetry(status, err)
|
||||||
|
if !retryOk {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
if r.attempts == maxRetryAttempts {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
r.attempts++
|
||||||
|
return r.bo.Pause(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldRetry(status int, err error) bool {
|
||||||
|
if 500 <= status && status <= 599 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if status == http.StatusTooManyRequests {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if err == io.ErrUnexpectedEOF {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Transient network errors should be retried.
|
||||||
|
if syscallRetryable(err) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if err, ok := err.(interface{ Temporary() bool }); ok {
|
||||||
|
if err.Temporary() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err, ok := err.(interface{ Unwrap() error }); ok {
|
||||||
|
return shouldRetry(status, err.Unwrap())
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
31
vendor/cloud.google.com/go/compute/metadata/retry_linux.go
generated
vendored
Normal file
31
vendor/cloud.google.com/go/compute/metadata/retry_linux.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2021 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Initialize syscallRetryable to return true on transient socket-level
|
||||||
|
// errors. These errors are specific to Linux.
|
||||||
|
syscallRetryable = func(err error) bool {
|
||||||
|
return errors.Is(err, syscall.ECONNRESET) || errors.Is(err, syscall.ECONNREFUSED)
|
||||||
|
}
|
||||||
|
}
|
||||||
28
vendor/cloud.google.com/go/compute/metadata/syscheck.go
generated
vendored
Normal file
28
vendor/cloud.google.com/go/compute/metadata/syscheck.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2024 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build !windows && !linux
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
// systemInfoSuggestsGCE reports whether the local system (without
|
||||||
|
// doing network requests) suggests that we're running on GCE. If this
|
||||||
|
// returns true, testOnGCE tries a bit harder to reach its metadata
|
||||||
|
// server.
|
||||||
|
//
|
||||||
|
// NOTE: systemInfoSuggestsGCE is assigned to a varible for test stubbing purposes.
|
||||||
|
var systemInfoSuggestsGCE = func() bool {
|
||||||
|
// We don't currently have checks for other GOOS
|
||||||
|
return false
|
||||||
|
}
|
||||||
30
vendor/cloud.google.com/go/compute/metadata/syscheck_linux.go
generated
vendored
Normal file
30
vendor/cloud.google.com/go/compute/metadata/syscheck_linux.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2024 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build linux
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NOTE: systemInfoSuggestsGCE is assigned to a varible for test stubbing purposes.
|
||||||
|
var systemInfoSuggestsGCE = func() bool {
|
||||||
|
b, _ := os.ReadFile("/sys/class/dmi/id/product_name")
|
||||||
|
|
||||||
|
name := strings.TrimSpace(string(b))
|
||||||
|
return name == "Google" || name == "Google Compute Engine"
|
||||||
|
}
|
||||||
39
vendor/cloud.google.com/go/compute/metadata/syscheck_windows.go
generated
vendored
Normal file
39
vendor/cloud.google.com/go/compute/metadata/syscheck_windows.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2024 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NOTE: systemInfoSuggestsGCE is assigned to a varible for test stubbing purposes.
|
||||||
|
var systemInfoSuggestsGCE = func() bool {
|
||||||
|
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\HardwareConfig\Current`, registry.QUERY_VALUE)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer k.Close()
|
||||||
|
|
||||||
|
s, _, err := k.GetStringValue("SystemProductName")
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
return strings.HasPrefix(s, "Google")
|
||||||
|
}
|
||||||
201
vendor/github.com/bytedance/gopkg/LICENSE
generated
vendored
Normal file
201
vendor/github.com/bytedance/gopkg/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
43
vendor/github.com/bytedance/gopkg/lang/dirtmake/bytes.go
generated
vendored
Normal file
43
vendor/github.com/bytedance/gopkg/lang/dirtmake/bytes.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2024 ByteDance Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package dirtmake
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type slice struct {
|
||||||
|
data unsafe.Pointer
|
||||||
|
len int
|
||||||
|
cap int
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname mallocgc runtime.mallocgc
|
||||||
|
func mallocgc(size uintptr, typ unsafe.Pointer, needzero bool) unsafe.Pointer
|
||||||
|
|
||||||
|
// Bytes allocates a byte slice but does not clean up the memory it references.
|
||||||
|
// Throw a fatal error instead of panic if cap is greater than runtime.maxAlloc.
|
||||||
|
// NOTE: MUST set any byte element before it's read.
|
||||||
|
func Bytes(len, cap int) (b []byte) {
|
||||||
|
if len < 0 || len > cap {
|
||||||
|
panic("dirtmake.Bytes: len out of range")
|
||||||
|
}
|
||||||
|
p := mallocgc(uintptr(cap), nil, false)
|
||||||
|
sh := (*slice)(unsafe.Pointer(&b))
|
||||||
|
sh.data = p
|
||||||
|
sh.len = len
|
||||||
|
sh.cap = cap
|
||||||
|
return
|
||||||
|
}
|
||||||
5
vendor/github.com/bytedance/sonic/.codespellrc
generated
vendored
Normal file
5
vendor/github.com/bytedance/sonic/.codespellrc
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[codespell]
|
||||||
|
# ignore test files, go project names, binary files via `skip` and special var/regex via `ignore-words`
|
||||||
|
skip = fuzz,*_test.tmpl,testdata,*_test.go,go.mod,go.sum,*.gz
|
||||||
|
ignore-words = .github/workflows/.ignore_words
|
||||||
|
check-filenames = true
|
||||||
55
vendor/github.com/bytedance/sonic/.gitignore
generated
vendored
Normal file
55
vendor/github.com/bytedance/sonic/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
*.o
|
||||||
|
*.swp
|
||||||
|
*.swm
|
||||||
|
*.swn
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
_testmain.go
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
*.rar
|
||||||
|
*.zip
|
||||||
|
*.gz
|
||||||
|
*.psd
|
||||||
|
*.bmd
|
||||||
|
*.cfg
|
||||||
|
*.pptx
|
||||||
|
*.log
|
||||||
|
*nohup.out
|
||||||
|
*settings.pyc
|
||||||
|
*.sublime-project
|
||||||
|
*.sublime-workspace
|
||||||
|
.DS_Store
|
||||||
|
/.idea/
|
||||||
|
/.vscode/
|
||||||
|
/output/
|
||||||
|
/vendor/
|
||||||
|
/Gopkg.lock
|
||||||
|
/Gopkg.toml
|
||||||
|
coverage.html
|
||||||
|
coverage.out
|
||||||
|
coverage.xml
|
||||||
|
junit.xml
|
||||||
|
*.profile
|
||||||
|
*.svg
|
||||||
|
*.out
|
||||||
|
ast/test.out
|
||||||
|
ast/bench.sh
|
||||||
|
|
||||||
|
!testdata/**/*.json.gz
|
||||||
|
fuzz/testdata
|
||||||
|
*__debug_bin*
|
||||||
|
*pprof
|
||||||
|
*coverage.txt
|
||||||
|
tools/venv/*
|
||||||
9
vendor/github.com/bytedance/sonic/.gitmodules
generated
vendored
Normal file
9
vendor/github.com/bytedance/sonic/.gitmodules
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[submodule "cloudwego"]
|
||||||
|
path = tools/asm2asm
|
||||||
|
url = https://github.com/cloudwego/asm2asm.git
|
||||||
|
[submodule "tools/simde"]
|
||||||
|
path = tools/simde
|
||||||
|
url = https://github.com/simd-everywhere/simde.git
|
||||||
|
[submodule "fuzz/go-fuzz-corpus"]
|
||||||
|
path = fuzz/go-fuzz-corpus
|
||||||
|
url = https://github.com/dvyukov/go-fuzz-corpus.git
|
||||||
24
vendor/github.com/bytedance/sonic/.licenserc.yaml
generated
vendored
Normal file
24
vendor/github.com/bytedance/sonic/.licenserc.yaml
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
header:
|
||||||
|
license:
|
||||||
|
spdx-id: Apache-2.0
|
||||||
|
copyright-owner: ByteDance Inc.
|
||||||
|
|
||||||
|
paths:
|
||||||
|
- '**/*.go'
|
||||||
|
- '**/*.s'
|
||||||
|
|
||||||
|
paths-ignore:
|
||||||
|
- 'ast/asm.s' # empty file
|
||||||
|
- 'decoder/asm.s' # empty file
|
||||||
|
- 'encoder/asm.s' # empty file
|
||||||
|
- 'internal/caching/asm.s' # empty file
|
||||||
|
- 'internal/jit/asm.s' # empty file
|
||||||
|
- 'internal/native/avx/native_amd64.s' # auto-generated by asm2asm
|
||||||
|
- 'internal/native/avx/native_subr_amd64.go' # auto-generated by asm2asm
|
||||||
|
- 'internal/native/avx2/native_amd64.s' # auto-generated by asm2asm
|
||||||
|
- 'internal/native/avx2/native_subr_amd64.go' # auto-generated by asm2asm
|
||||||
|
- 'internal/resolver/asm.s' # empty file
|
||||||
|
- 'internal/rt/asm.s' # empty file
|
||||||
|
- 'internal/loader/asm.s' # empty file
|
||||||
|
|
||||||
|
comment: on-failure
|
||||||
128
vendor/github.com/bytedance/sonic/CODE_OF_CONDUCT.md
generated
vendored
Normal file
128
vendor/github.com/bytedance/sonic/CODE_OF_CONDUCT.md
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
wudi.daniel@bytedance.com.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
||||||
63
vendor/github.com/bytedance/sonic/CONTRIBUTING.md
generated
vendored
Normal file
63
vendor/github.com/bytedance/sonic/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# How to Contribute
|
||||||
|
|
||||||
|
## Your First Pull Request
|
||||||
|
We use GitHub for our codebase. You can start by reading [How To Pull Request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests).
|
||||||
|
|
||||||
|
## Without Semantic Versioning
|
||||||
|
We keep the stable code in branch `main` like `golang.org/x`. Development base on branch `develop`. We promise the **Forward Compatibility** by adding new package directory with suffix `v2/v3` when code has break changes.
|
||||||
|
|
||||||
|
## Branch Organization
|
||||||
|
We use [git-flow](https://nvie.com/posts/a-successful-git-branching-model/) as our branch organization, as known as [FDD](https://en.wikipedia.org/wiki/Feature-driven_development)
|
||||||
|
|
||||||
|
|
||||||
|
## Bugs
|
||||||
|
### 1. How to Find Known Issues
|
||||||
|
We are using [Github Issues](https://github.com/bytedance/sonic/issues) for our public bugs. We keep a close eye on this and try to make it clear when we have an internal fix in progress. Before filing a new task, try to make sure your problem doesn’t already exist.
|
||||||
|
|
||||||
|
### 2. Reporting New Issues
|
||||||
|
Providing a reduced test code is a recommended way for reporting issues. Then can be placed in:
|
||||||
|
- Just in issues
|
||||||
|
- [Golang Playground](https://play.golang.org/)
|
||||||
|
|
||||||
|
### 3. Security Bugs
|
||||||
|
Please do not report the safe disclosure of bugs to public issues. Contact us by [Support Email](mailto:sonic@bytedance.com)
|
||||||
|
|
||||||
|
## How to Get in Touch
|
||||||
|
- [Email](mailto:wudi.daniel@bytedance.com)
|
||||||
|
|
||||||
|
## Submit a Pull Request
|
||||||
|
Before you submit your Pull Request (PR) consider the following guidelines:
|
||||||
|
1. Search [GitHub](https://github.com/bytedance/sonic/pulls) for an open or closed PR that relates to your submission. You don't want to duplicate existing efforts.
|
||||||
|
2. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add. Discussing the design upfront helps to ensure that we're ready to accept your work.
|
||||||
|
3. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the bytedance/sonic repo.
|
||||||
|
4. In your forked repository, make your changes in a new git branch:
|
||||||
|
```
|
||||||
|
git checkout -b bugfix/security_bug develop
|
||||||
|
```
|
||||||
|
5. Create your patch, including appropriate test cases.
|
||||||
|
6. Follow our [Style Guides](#code-style-guides).
|
||||||
|
7. Commit your changes using a descriptive commit message that follows [AngularJS Git Commit Message Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit).
|
||||||
|
Adherence to these conventions is necessary because release notes will be automatically generated from these messages.
|
||||||
|
8. Push your branch to GitHub:
|
||||||
|
```
|
||||||
|
git push origin bugfix/security_bug
|
||||||
|
```
|
||||||
|
9. In GitHub, send a pull request to `sonic:main`
|
||||||
|
|
||||||
|
Note: you must use one of `optimize/feature/bugfix/doc/ci/test/refactor` following a slash(`/`) as the branch prefix.
|
||||||
|
|
||||||
|
Your pr title and commit message should follow https://www.conventionalcommits.org/.
|
||||||
|
|
||||||
|
## Contribution Prerequisites
|
||||||
|
- Our development environment keeps up with [Go Official](https://golang.org/project/).
|
||||||
|
- You need fully checking with lint tools before submit your pull request. [gofmt](https://golang.org/pkg/cmd/gofmt/) & [golangci-lint](https://github.com/golangci/golangci-lint)
|
||||||
|
- You are familiar with [Github](https://github.com)
|
||||||
|
- Maybe you need familiar with [Actions](https://github.com/features/actions)(our default workflow tool).
|
||||||
|
|
||||||
|
## Code Style Guides
|
||||||
|
See [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
||||||
|
|
||||||
|
Good resources:
|
||||||
|
- [Effective Go](https://golang.org/doc/effective_go)
|
||||||
|
- [Pingcap General advice](https://pingcap.github.io/style-guide/general.html)
|
||||||
|
- [Uber Go Style Guide](https://github.com/uber-go/guide/blob/master/style.md)
|
||||||
0
vendor/github.com/bytedance/sonic/CREDITS
generated
vendored
Normal file
0
vendor/github.com/bytedance/sonic/CREDITS
generated
vendored
Normal file
201
vendor/github.com/bytedance/sonic/LICENSE
generated
vendored
Normal file
201
vendor/github.com/bytedance/sonic/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user