diff --git a/authentication.go b/authentication.go new file mode 100644 index 0000000..f7c90ca --- /dev/null +++ b/authentication.go @@ -0,0 +1,261 @@ +package apirouter + +import ( + "net/http" + "strings" + "time" + + "github.com/golang-jwt/jwt" +) + +const ( + defaultExpiration = 1 * time.Hour + + // AuthorizationHeader is the auth header + AuthorizationHeader = "Authorization" + + // AuthorizationBearer is the second part of the auth header + AuthorizationBearer = "Bearer" + + // CookieName is for the secure cookie that also has the JWT token + CookieName = "jwt_token" +) + +// Claims is our custom JWT claims +type Claims struct { + jwt.StandardClaims // The standard JWT fields + UserID string `json:"user_id"` // The user ID set on the claims +} + +// CreateToken will make a token from claims +func (c Claims) CreateToken(expiration time.Duration, sessionSecret string) (string, error) { + + // Create a new token object, specifying signing method, and the claims + token := jwt.NewWithClaims(jwt.SigningMethodHS256, createClaims(c.UserID, c.Issuer, c.Id, expiration)) + + // Sign and get the complete encoded token as a string using the secret + return token.SignedString([]byte(sessionSecret)) +} + +// Verify will check the claims against known verifications +func (c Claims) Verify(issuer string) (bool, error) { + + // Invalid issuer + if c.Issuer != issuer { + return false, ErrIssuerMismatch + } + + // Valid Session ID + if len(c.Id) == 0 { + return false, ErrInvalidSessionID + } + + // Valid User ID + if c.UserID == "" { + return false, ErrInvalidUserID + } + + return true, nil +} + +// IsEmpty will detect if the claims are empty or not +func (c Claims) IsEmpty() bool { + return len(c.UserID) <= 0 +} + +// createClaims will make a new set of claims for JWT +func createClaims(userID, issuer, sessionID string, expiration time.Duration) Claims { + // Set default if not set + if expiration <= 0 { + expiration = defaultExpiration + } + return Claims{ + jwt.StandardClaims{ + ExpiresAt: time.Now().Add(expiration).UTC().Unix(), + Id: sessionID, + IssuedAt: time.Now().UTC().Unix(), + Issuer: issuer, + NotBefore: time.Now().UTC().Unix(), + }, + userID, + } +} + +// CreateToken will make the claims, and then make/sign the token +func CreateToken(sessionSecret, userID, issuer, sessionID string, + expiration time.Duration) (string, error) { + + // Create a new token object, specifying signing method, and the claims + token := jwt.NewWithClaims(jwt.SigningMethodHS256, createClaims(userID, issuer, sessionID, expiration)) + + // Sign and get the complete encoded token as a string using the secret + return token.SignedString([]byte(sessionSecret)) +} + +// ClearToken will remove the token from the response and request +func ClearToken(w http.ResponseWriter, req *http.Request) { + + // Remove from response + w.Header().Del(AuthorizationHeader) + + // Create empty cookie + cookie := &http.Cookie{ + Path: "/", + Name: CookieName, + Value: "", + Expires: time.Now().Add(-24 * time.Hour), + } + + // Remove from request + if req != nil && req.Header != nil { + req.Header.Del(AuthorizationHeader) + req.Header.Del("Cookie") // Remove all cookies + req.AddCookie(cookie) // Add the empty cookie + } + + // Clear any cookie out + http.SetCookie(w, cookie) +} + +// Check will check if the JWT is present and valid in the request and then extend the token +func Check(w http.ResponseWriter, r *http.Request, sessionSecret, issuer string, + sessionAge time.Duration) (authenticated bool, req *http.Request, err error) { + + var jwtToken string + + // Look for a cookie value first + var cookie *http.Cookie + cookie, _ = r.Cookie(CookieName) + if cookie != nil { + jwtToken = cookie.Value + } else { // Get from the auth header + authHeaderValue := r.Header.Get(AuthorizationHeader) + authHeader := strings.Split(authHeaderValue, AuthorizationBearer+" ") + if len(authHeader) != 2 { + err = ErrHeaderInvalid + return + } + // Set the token value + jwtToken = authHeader[1] + } + + // Parse the JWT token + var token *jwt.Token + if token, err = jwt.ParseWithClaims(jwtToken, &Claims{}, func(token *jwt.Token) (interface{}, error) { + // This error NEVER occurs since we are using our own signing method + /*if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + // return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + return nil, ErrUnexpectedSigningMethod + }*/ + return []byte(sessionSecret), nil + }); err != nil { + return + } + + // This NEVER occurs, so far all errors have been returned first so the above case would get hit + /* else if token == nil { + // err = fmt.Errorf("token was nil from: %s", jwtToken) + err = ErrTokenNil + return + }*/ + + // Check we have claims and validity of token + if claims, ok := token.Claims.(*Claims); ok && token.Valid { + + // Now verify the claims are good + if _, claimErr := claims.Verify(issuer); claimErr != nil { + // err = fmt.Errorf("claims failed validation: %w", claimErr) + err = ErrClaimsValidationFailed + return + } + + // This case is NEVER hit since there is always a corresponding error + /* else if !verified { + err = ErrClaimsValidationFailed + return + }*/ + + // Create new token + var newToken string + if newToken, err = CreateToken( + sessionSecret, + claims.UserID, + issuer, + claims.Id, + sessionAge, + ); err != nil { + return + } + + // Set the token in the writer (response) + SetTokenHeader(w, r, newToken, sessionAge) + + // Add the claims to the request for future use in router actions + req = SetCustomData(r, claims) + authenticated = true + } else { // Not sure how or why this error would be triggered... + err = ErrJWTInvalid + } + + return +} + +// GetClaims will return the current claims from the request +func GetClaims(req *http.Request) Claims { + if claims := GetCustomData(req); claims != nil { + return *claims.(*Claims) + } + return Claims{} +} + +// GetTokenFromHeaderFromRequest will get the token value from the request +func GetTokenFromHeaderFromRequest(req *http.Request) string { + headerVal := req.Header.Get(AuthorizationHeader) + if parts := strings.Split(headerVal, " "); len(parts) > 1 { + return parts[1] + } + return "" +} + +// GetTokenFromHeader will get the token value from the header +func GetTokenFromHeader(w http.ResponseWriter) string { + headerVal := w.Header().Get(AuthorizationHeader) + if parts := strings.Split(headerVal, " "); len(parts) > 1 { + return parts[1] + } + return "" +} + +// GetTokenFromResponse will get the token value from the HTTP response +func GetTokenFromResponse(res *http.Response) string { + headerVal := res.Header.Get(AuthorizationHeader) + if parts := strings.Split(headerVal, " "); len(parts) > 1 { + return parts[1] + } + return "" +} + +// SetTokenHeader will set the authentication token on the response and set a cookie +func SetTokenHeader(w http.ResponseWriter, r *http.Request, token string, expiration time.Duration) { + + // Set on the response + w.Header().Set(AuthorizationHeader, AuthorizationBearer+" "+token) + + // Set on the request + r.Header.Set(AuthorizationHeader, AuthorizationBearer+" "+token) + + // Create the cookie + cookie := &http.Cookie{ + Path: "/", + Name: CookieName, + Value: token, + Expires: time.Now().UTC().Add(expiration), + // todo: secure / http only etc + } + + // Set the cookie on the request + r.AddCookie(cookie) + + // Set the cookie (response) + http.SetCookie(w, cookie) +} diff --git a/authentication_test.go b/authentication_test.go new file mode 100644 index 0000000..8002a66 --- /dev/null +++ b/authentication_test.go @@ -0,0 +1,553 @@ +package apirouter + +import ( + "context" + "crypto/rand" + "encoding/hex" + "io" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestSetTokenHeader will test the method SetTokenHeader() +func TestSetTokenHeader(t *testing.T) { + t.Parallel() + + t.Run("nil writer and req, panic", func(t *testing.T) { + assert.Panics(t, func() { + SetTokenHeader(nil, nil, "", 3*time.Minute) + }) + }) + + t.Run("writer, no req, no token", func(t *testing.T) { + w := httptest.NewRecorder() + assert.Panics(t, func() { + SetTokenHeader(w, nil, "", 3*time.Minute) + }) + }) + + t.Run("writer, req, no token", func(t *testing.T) { + w := httptest.NewRecorder() + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "https://domain.com", nil) + assert.NoError(t, err) + assert.NotNil(t, req) + SetTokenHeader(w, req, "", 3*time.Minute) + }) + + t.Run("writer, req, valid token", func(t *testing.T) { + w := httptest.NewRecorder() + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "https://domain.com", nil) + assert.NoError(t, err) + assert.NotNil(t, req) + SetTokenHeader(w, req, "token", 3*time.Minute) + assert.Equal(t, AuthorizationBearer+" token", w.Header().Get(AuthorizationHeader)) + assert.Equal(t, AuthorizationBearer+" token", req.Header.Get(AuthorizationHeader)) + + var cookie *http.Cookie + cookie, err = req.Cookie(CookieName) + require.NoError(t, err) + require.NotNil(t, cookie) + assert.Equal(t, "token", cookie.Value) + }) +} + +// TestGetTokenFromHeader will test the method GetTokenFromHeader() +func TestGetTokenFromHeader(t *testing.T) { + t.Parallel() + + t.Run("nil writer, panic", func(t *testing.T) { + assert.Panics(t, func() { + token := GetTokenFromHeader(nil) + assert.Equal(t, "", token) + }) + }) + + t.Run("empty token", func(t *testing.T) { + w := httptest.NewRecorder() + token := GetTokenFromHeader(w) + assert.Equal(t, "", token) + }) + + t.Run("get valid token", func(t *testing.T) { + w := httptest.NewRecorder() + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "https://domain.com", nil) + assert.NoError(t, err) + assert.NotNil(t, req) + SetTokenHeader(w, req, "token", 3*time.Minute) + token := GetTokenFromHeader(w) + assert.Equal(t, "token", token) + }) +} + +// TestGetTokenFromResponse will test the method GetTokenFromResponse() +func TestGetTokenFromResponse(t *testing.T) { + t.Parallel() + + t.Run("nil writer, panic", func(t *testing.T) { + assert.Panics(t, func() { + token := GetTokenFromResponse(nil) + assert.Equal(t, "", token) + }) + }) + + t.Run("empty token", func(t *testing.T) { + w := httptest.NewRecorder() + res := w.Result() + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(res.Body) + token := GetTokenFromResponse(res) + assert.Equal(t, "", token) + }) + + t.Run("get valid token", func(t *testing.T) { + w := httptest.NewRecorder() + req, err := http.NewRequestWithContext( + context.Background(), http.MethodGet, "https://domain.com", nil) + assert.NoError(t, err) + assert.NotNil(t, req) + SetTokenHeader(w, req, "token", 3*time.Minute) + res := w.Result() + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(res.Body) + token := GetTokenFromResponse(res) + assert.Equal(t, "token", token) + }) +} + +// TestClearToken will test the method ClearToken() +func TestClearToken(t *testing.T) { + t.Parallel() + + t.Run("nil writer, panic", func(t *testing.T) { + assert.Panics(t, func() { + ClearToken(nil, nil) + }) + }) + + t.Run("empty token", func(t *testing.T) { + w := httptest.NewRecorder() + ClearToken(w, nil) + }) + + t.Run("clear valid token", func(t *testing.T) { + w := httptest.NewRecorder() + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "https://domain.com", nil) + assert.NoError(t, err) + assert.NotNil(t, req) + SetTokenHeader(w, req, "token", 3*time.Minute) + + token := GetTokenFromHeader(w) + assert.Equal(t, "token", token) + + token2 := GetTokenFromHeaderFromRequest(req) + assert.Equal(t, "token", token2) + + var cookie *http.Cookie + cookie, err = req.Cookie(CookieName) + require.NotNil(t, cookie) + require.NoError(t, err) + + token3 := cookie.Value + assert.Equal(t, "token", token3) + + ClearToken(w, req) + + token = GetTokenFromHeader(w) + assert.Equal(t, "", token) + + token2 = GetTokenFromHeaderFromRequest(req) + assert.Equal(t, "", token2) + + cookie, err = req.Cookie(CookieName) + require.NotNil(t, cookie) + require.NoError(t, err) + token3 = cookie.Value + assert.Equal(t, "", token3) + }) +} + +// TestClaims_CreateToken will test the method CreateToken() +func TestClaims_CreateToken(t *testing.T) { + t.Parallel() + + sessionID, err := randomHex(32) + assert.NoError(t, err) + + var secret string + secret, err = randomHex(16) + assert.NoError(t, err) + assert.NotEqual(t, "", secret) + + t.Run("valid token", func(t *testing.T) { + claims := createClaims( + "123", + "web-server-test", + sessionID, + 5*time.Minute, + ) + + var token string + token, err = claims.CreateToken(5*time.Minute, secret) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + + var valid bool + valid, err = claims.Verify("web-server-test") + assert.NoError(t, err) + assert.Equal(t, true, valid) + + assert.Equal(t, false, claims.IsEmpty()) + }) + + t.Run("user id is empty", func(t *testing.T) { + claims := createClaims( + "", + "web-server-test", + sessionID, + 5*time.Minute, + ) + + var token string + token, err = claims.CreateToken(5*time.Minute, secret) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + + assert.Equal(t, true, claims.IsEmpty()) + }) + + t.Run("missing user id", func(t *testing.T) { + claims := createClaims( + "", + "web-server-test", + sessionID, + 5*time.Minute, + ) + + var token string + token, err = claims.CreateToken(5*time.Minute, secret) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + + var valid bool + valid, err = claims.Verify("web-server-test") + assert.Error(t, err) + assert.Equal(t, false, valid) + }) + + t.Run("wrong issuer", func(t *testing.T) { + claims := createClaims( + "123", + "web-server-test", + sessionID, + 5*time.Minute, + ) + + var token string + token, err = claims.CreateToken(5*time.Minute, secret) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + + var valid bool + valid, err = claims.Verify("web-server-wrong") + assert.Error(t, err) + assert.Equal(t, false, valid) + }) + + t.Run("missing session id", func(t *testing.T) { + claims := createClaims( + "123", + "web-server-test", + "", + 5*time.Minute, + ) + + var token string + token, err = claims.CreateToken(5*time.Minute, secret) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + + var valid bool + valid, err = claims.Verify("web-server-test") + assert.Error(t, err) + assert.Equal(t, false, valid) + }) + + t.Run("valid - empty expiration, set default", func(t *testing.T) { + claims := createClaims( + "123", + "web-server-test", + sessionID, + 0, + ) + + var token string + token, err = claims.CreateToken(5*time.Minute, secret) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + + var valid bool + valid, err = claims.Verify("web-server-test") + assert.NoError(t, err) + assert.Equal(t, true, valid) + }) + +} + +// TestCreateToken will test the method CreateToken() +func TestCreateToken(t *testing.T) { + t.Parallel() + + sessionID, err := randomHex(16) + assert.NoError(t, err) + + var secret string + secret, err = randomHex(16) + assert.NoError(t, err) + assert.NotEqual(t, "", secret) + + t.Run("valid token", func(t *testing.T) { + + var token string + token, err = CreateToken( + secret, + "123", + "web-server-test", + sessionID, + 5*time.Minute, + ) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + }) + + t.Run("missing user id", func(t *testing.T) { + + var token string + token, err = CreateToken( + secret, + "123", + "web-server-test", + sessionID, + 5*time.Minute, + ) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + }) + + t.Run("missing issuer", func(t *testing.T) { + + var token string + token, err = CreateToken( + secret, + "123", + "", + sessionID, + 5*time.Minute, + ) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + }) + + t.Run("missing session id", func(t *testing.T) { + + var token string + token, err = CreateToken( + secret, + "123", + "web-server-test", + "", + 5*time.Minute, + ) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + }) + + t.Run("missing secret", func(t *testing.T) { + + var token string + token, err = CreateToken( + "", + "123", + "web-server-test", + sessionID, + 5*time.Minute, + ) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + }) + + t.Run("create token - verify", func(t *testing.T) { + + var token string + token, err = CreateToken( + secret, + "123", + "web-server-test", + sessionID, + 5*time.Minute, + ) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + + w := httptest.NewRecorder() + + var req *http.Request + req, err = http.NewRequestWithContext(context.Background(), http.MethodGet, "https://domain.com", nil) + assert.NoError(t, err) + assert.NotNil(t, req) + + req.Header.Add(AuthorizationHeader, AuthorizationBearer+" "+token) + + var authenticated bool + authenticated, req, err = Check(w, req, secret, "web-server-test", 10) + assert.NoError(t, err) + assert.NotNil(t, req) + assert.Equal(t, true, authenticated) + + reqClaims := GetClaims(req) + assert.Equal(t, "123", reqClaims.UserID) + assert.Equal(t, sessionID, reqClaims.Id) + assert.Equal(t, "web-server-test", reqClaims.Issuer) + assert.WithinDuration(t, time.Now().UTC().Add(5*time.Minute), time.Unix(reqClaims.ExpiresAt, 0), 5*time.Second) + }) + + t.Run("verify - missing token in header", func(t *testing.T) { + + var token string + token, err = CreateToken( + secret, + "123", + "web-server-test", + sessionID, + 5*time.Minute, + ) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + + w := httptest.NewRecorder() + + var req *http.Request + req, err = http.NewRequestWithContext(context.Background(), http.MethodGet, "https://domain.com", nil) + assert.NoError(t, err) + assert.NotNil(t, req) + + var authenticated bool + authenticated, req, err = Check(w, req, secret, "web-server-test", 10) + assert.Error(t, err) + assert.Nil(t, req) + assert.Equal(t, false, authenticated) + }) + + t.Run("verify - invalid token", func(t *testing.T) { + + var token string + token, err = CreateToken( + secret, + "123", + "web-server-test", + sessionID, + 5*time.Minute, + ) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + + w := httptest.NewRecorder() + + var req *http.Request + req, err = http.NewRequestWithContext(context.Background(), http.MethodGet, "https://domain.com", nil) + assert.NoError(t, err) + assert.NotNil(t, req) + + req.Header.Add(AuthorizationHeader, AuthorizationBearer+" "+token+"-invalid") + + var authenticated bool + authenticated, req, err = Check(w, req, secret, "web-server-test", 10) + assert.Error(t, err) + assert.Nil(t, req) + assert.Equal(t, false, authenticated) + }) + + t.Run("verify - invalid issuer", func(t *testing.T) { + + var token string + token, err = CreateToken( + secret, + "123", + "web-server-test", + sessionID, + 5*time.Minute, + ) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + + w := httptest.NewRecorder() + + var req *http.Request + req, err = http.NewRequestWithContext(context.Background(), http.MethodGet, "https://domain.com", nil) + assert.NoError(t, err) + assert.NotNil(t, req) + + req.Header.Add(AuthorizationHeader, AuthorizationBearer+" "+token) + + var authenticated bool + authenticated, req, err = Check(w, req, secret, "web-server-wrong", 10) + assert.Error(t, err) + assert.Nil(t, req) + assert.Equal(t, false, authenticated) + }) + + t.Run("verify - invalid expiration time", func(t *testing.T) { + + var token string + token, err = CreateToken( + secret, + "", + "web-server-test", + sessionID, + 1*time.Nanosecond, + ) + assert.NoError(t, err) + assert.NotEqual(t, 0, len(token)) + + w := httptest.NewRecorder() + + var req *http.Request + req, err = http.NewRequestWithContext(context.Background(), http.MethodGet, "https://domain.com", nil) + assert.NoError(t, err) + assert.NotNil(t, req) + + req.Header.Add(AuthorizationHeader, AuthorizationBearer+" "+token) + + time.Sleep(1 * time.Second) + + var authenticated bool + authenticated, req, err = Check(w, req, secret, "web-server-test", 10) + assert.Error(t, err) + assert.Contains(t, err.Error(), "token is expired by") + assert.Nil(t, req) + assert.Equal(t, false, authenticated) + }) +} + +// TestGetClaims will test the method GetClaims() +func TestGetClaims(t *testing.T) { + req := httptest.NewRequest(http.MethodConnect, "/", nil) + claims := GetClaims(req) + assert.NotNil(t, claims) + assert.Equal(t, true, claims.IsEmpty()) +} + +// randomHex returns a random hex string, or an error and empty string +func randomHex(n int) (string, error) { + b := make([]byte, n) + if _, err := rand.Read(b); err != nil { + return "", err + } + return hex.EncodeToString(b), nil +} diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..38ffb34 --- /dev/null +++ b/errors.go @@ -0,0 +1,21 @@ +package apirouter + +import "errors" + +// ErrHeaderInvalid is when the header is missing or invalid +var ErrHeaderInvalid = errors.New("authorization header was mal-formatted or missing") + +// ErrClaimsValidationFailed is when the claim's validation has failed +var ErrClaimsValidationFailed = errors.New("claims failed validation") + +// ErrJWTInvalid is when the JWT payload is invalid +var ErrJWTInvalid = errors.New("jwt was invalid") + +// ErrIssuerMismatch is when the issuer does not match the system issuer +var ErrIssuerMismatch = errors.New("issuer did not match") + +// ErrInvalidSessionID is when the session id is invalid or missing +var ErrInvalidSessionID = errors.New("invalid session id detected") + +// ErrInvalidUserID is when the user ID is invalid or missing +var ErrInvalidUserID = errors.New("invalid user id detected") diff --git a/go.mod b/go.mod index ed48579..a8a3885 100644 --- a/go.mod +++ b/go.mod @@ -4,18 +4,22 @@ go 1.17 require ( github.com/gofrs/uuid v4.3.1+incompatible + github.com/golang-jwt/jwt v3.2.2+incompatible github.com/julienschmidt/httprouter v1.3.0 github.com/matryer/respond v1.0.1 github.com/mrz1836/go-logger v0.3.1 github.com/mrz1836/go-parameters v0.2.9 github.com/newrelic/go-agent/v3 v3.20.1 github.com/newrelic/go-agent/v3/integrations/nrhttprouter v1.0.1 + github.com/stretchr/testify v1.8.0 ) require ( github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/gorilla/mux v1.8.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/ugorji/go/codec v1.2.7 // indirect golang.org/x/net v0.2.0 // indirect golang.org/x/sys v0.2.0 // indirect @@ -23,4 +27,5 @@ require ( google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect google.golang.org/grpc v1.51.0 // indirect google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 9d5a07c..5e1adc8 100644 --- a/go.sum +++ b/go.sum @@ -1,58 +1,19 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927 h1:SKI1/fuSdodxmNNyVBR8d7X/HuLnRpvvFO0AgyQk764= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= -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-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/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.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-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 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/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.5.0/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/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/matryer/respond v1.0.1 h1:RSG07jdn32pH46t4UO1TnpnKlR/ayIpEa4aiK2f9k1U= @@ -68,117 +29,32 @@ github.com/newrelic/go-agent/v3/integrations/nrhttprouter v1.0.1 h1:7OyJ+5MXP8Yp github.com/newrelic/go-agent/v3/integrations/nrhttprouter v1.0.1/go.mod h1:CkLa4BKOGaiFbWHsv7zOspG+zXrfjsNXDxsDGvdhT+s= 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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/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-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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220907135653-1e95f45603a7 h1:1WGATo9HAhkWMbfyuVU0tEFP88OIkUvwaHFveQPvzCQ= -golang.org/x/net v0.0.0-20220907135653-1e95f45603a7/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220907062415-87db552b00fd h1:AZeIEzg+8RCELJYq8w+ODLVxFgLMMigSwO/ffKPEd9U= -golang.org/x/sys v0.0.0-20220907062415-87db552b00fd/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 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/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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20220902135211-223410557253 h1:vXJMM8Shg7TGaYxZsQ++A/FOSlbDmDtWhS/o+3w/hj4= -google.golang.org/genproto v0.0.0-20220902135211-223410557253/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c= google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -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.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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=