From a2fc4ec22191b6e95a2ad0770dd483d97df3feb2 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Tue, 22 Mar 2022 13:12:11 +0100 Subject: [PATCH] Rotate Access token with refresh token (#280) Add method for rotating access token with refresh tokens This will be useful for catching expired sessions and offboarding users Also added functions to handle secrets. They have to be revisited as some tests didn't run on CI as they waited some user input, like password --- .github/workflows/golang-test-darwin.yml | 2 +- .github/workflows/golang-test-linux.yml | 15 +-- client/internal/oauth/auth0.go | 106 +++++++++++++++----- client/internal/oauth/auth0_test.go | 121 ++++++++++++++++++++++- client/internal/oauth/oauth.go | 1 + client/internal/oauth/secret.go | 66 +++++++++++++ client/internal/oauth/secret_test.go | 27 +++++ go.mod | 10 +- go.sum | 27 ++++- 9 files changed, 329 insertions(+), 46 deletions(-) create mode 100644 client/internal/oauth/secret.go create mode 100644 client/internal/oauth/secret_test.go diff --git a/.github/workflows/golang-test-darwin.yml b/.github/workflows/golang-test-darwin.yml index 654048934..2311c54ff 100644 --- a/.github/workflows/golang-test-darwin.yml +++ b/.github/workflows/golang-test-darwin.yml @@ -26,4 +26,4 @@ jobs: run: go mod tidy - name: Test - run: go test -exec sudo -timeout 5m -p 1 ./... \ No newline at end of file + run: go test -exec 'sudo --preserve-env=CI' -timeout 5m -p 1 ./... \ No newline at end of file diff --git a/.github/workflows/golang-test-linux.yml b/.github/workflows/golang-test-linux.yml index 9f20682a4..f94696105 100644 --- a/.github/workflows/golang-test-linux.yml +++ b/.github/workflows/golang-test-linux.yml @@ -11,18 +11,7 @@ jobs: uses: actions/setup-go@v2 with: go-version: ${{ matrix.go-version }} -# - name: update limits.d -# run: | -# cat <<'EOF' | sudo tee -a /etc/security/limits.d/wt.conf -# root soft nproc 65535 -# root hard nproc 65535 -# root soft nofile 65535 -# root hard nofile 65535 -# $(whoami) soft nproc 65535 -# $(whoami) hard nproc 65535 -# $(whoami) soft nofile 65535 -# $(whoami) hard nofile 65535 -# EOF + - name: Cache Go modules uses: actions/cache@v2 @@ -42,4 +31,4 @@ jobs: run: go mod tidy - name: Test - run: go test -exec sudo -timeout 5m -p 1 ./... + run: go test -exec 'sudo --preserve-env=CI' -timeout 5m -p 1 ./... diff --git a/client/internal/oauth/auth0.go b/client/internal/oauth/auth0.go index 3370994bd..2f24f0979 100644 --- a/client/internal/oauth/auth0.go +++ b/client/internal/oauth/auth0.go @@ -12,7 +12,10 @@ import ( ) // auth0GrantType grant type for device flow on Auth0 -const auth0GrantType = "urn:ietf:params:oauth:grant-type:device_code" +const ( + auth0GrantType = "urn:ietf:params:oauth:grant-type:device_code" + auth0RefreshGrant = "refresh_token" +) // Auth0 client type Auth0 struct { @@ -34,9 +37,10 @@ type RequestDeviceCodePayload struct { // TokenRequestPayload used for requesting the auth0 token type TokenRequestPayload struct { - GrantType string `json:"grant_type"` - DeviceCode string `json:"device_code"` - ClientID string `json:"client_id"` + GrantType string `json:"grant_type"` + DeviceCode string `json:"device_code,omitempty"` + ClientID string `json:"client_id"` + RefreshToken string `json:"refresh_token,omitempty"` } // TokenRequestResponse used for parsing Auth0 token's response @@ -48,7 +52,7 @@ type TokenRequestResponse struct { // Claims used when validating the access token type Claims struct { - Audiance string `json:"aud"` + Audience string `json:"aud"` } // NewAuth0DeviceFlow returns an Auth0 OAuth client @@ -127,30 +131,13 @@ func (a *Auth0) WaitToken(ctx context.Context, info DeviceAuthInfo) (TokenInfo, DeviceCode: info.DeviceCode, ClientID: a.ClientID, } - p, err := json.Marshal(tokenReqPayload) + + body, statusCode, err := requestToken(a.HTTPClient, url, tokenReqPayload) if err != nil { - return TokenInfo{}, fmt.Errorf("parsing token payload failed with error: %v", err) - } - payload := strings.NewReader(string(p)) - req, err := http.NewRequest("POST", url, payload) - if err != nil { - return TokenInfo{}, fmt.Errorf("creating wait token request failed with error: %v", err) + return TokenInfo{}, fmt.Errorf("wait for token: %v", err) } - req.Header.Add("content-type", "application/json") - - res, err := a.HTTPClient.Do(req) - if err != nil { - return TokenInfo{}, fmt.Errorf("doing wiat request failed with error: %v", err) - } - - defer res.Body.Close() - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return TokenInfo{}, fmt.Errorf("reading toekn body failed with error: %v", err) - } - - if res.StatusCode > 499 { + if statusCode > 499 { return TokenInfo{}, fmt.Errorf("wait token code returned error: %s", string(body)) } @@ -184,6 +171,71 @@ func (a *Auth0) WaitToken(ctx context.Context, info DeviceAuthInfo) (TokenInfo, } } +// RotateAccessToken requests a new token using an existing refresh token +func (a *Auth0) RotateAccessToken(ctx context.Context, refreshToken string) (TokenInfo, error) { + url := "https://" + a.Domain + "/oauth/token" + tokenReqPayload := TokenRequestPayload{ + GrantType: auth0RefreshGrant, + ClientID: a.ClientID, + RefreshToken: refreshToken, + } + + body, statusCode, err := requestToken(a.HTTPClient, url, tokenReqPayload) + if err != nil { + return TokenInfo{}, fmt.Errorf("rotate access token: %v", err) + } + + if statusCode != 200 { + return TokenInfo{}, fmt.Errorf("rotating token returned error: %s", string(body)) + } + + tokenResponse := TokenRequestResponse{} + err = json.Unmarshal(body, &tokenResponse) + if err != nil { + return TokenInfo{}, fmt.Errorf("parsing token response failed with error: %v", err) + } + + err = isValidAccessToken(tokenResponse.AccessToken, a.Audience) + if err != nil { + return TokenInfo{}, fmt.Errorf("validate access token failed with error: %v", err) + } + + tokenInfo := TokenInfo{ + AccessToken: tokenResponse.AccessToken, + TokenType: tokenResponse.TokenType, + RefreshToken: tokenResponse.RefreshToken, + IDToken: tokenResponse.IDToken, + ExpiresIn: tokenResponse.ExpiresIn, + } + return tokenInfo, err +} + +func requestToken(client HTTPClient, url string, tokenReqPayload TokenRequestPayload) ([]byte, int, error) { + p, err := json.Marshal(tokenReqPayload) + if err != nil { + return nil, 0, fmt.Errorf("parsing token payload failed with error: %v", err) + } + payload := strings.NewReader(string(p)) + req, err := http.NewRequest("POST", url, payload) + if err != nil { + return nil, 0, fmt.Errorf("creating token request failed with error: %v", err) + } + + req.Header.Add("content-type", "application/json") + + res, err := client.Do(req) + if err != nil { + return nil, 0, fmt.Errorf("doing token request failed with error: %v", err) + } + + defer res.Body.Close() + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, 0, fmt.Errorf("reading token body failed with error: %v", err) + } + return body, res.StatusCode, nil +} + // isValidAccessToken is a simple validation of the access token func isValidAccessToken(token string, audience string) error { if token == "" { @@ -202,7 +254,7 @@ func isValidAccessToken(token string, audience string) error { return err } - if claims.Audiance != audience { + if claims.Audience != audience { return fmt.Errorf("invalid audience") } diff --git a/client/internal/oauth/auth0_test.go b/client/internal/oauth/auth0_test.go index 2246b166e..6fac44fa3 100644 --- a/client/internal/oauth/auth0_test.go +++ b/client/internal/oauth/auth0_test.go @@ -152,7 +152,6 @@ func TestAuth0_WaitToken(t *testing.T) { Interval: 1, } - // payload,timeout,error 500,error conn,error response, invalid token,good req tokenReqPayload := TokenRequestPayload{ GrantType: auth0GrantType, DeviceCode: defaultInfo.DeviceCode, @@ -294,3 +293,123 @@ func TestAuth0_WaitToken(t *testing.T) { }) } } + +func TestAuth0_RotateAccessToken(t *testing.T) { + type test struct { + name string + inputResBody string + inputReqCode int + inputReqError error + inputMaxReqs int + inputInfo DeviceAuthInfo + inputAudience string + testingErrFunc require.ErrorAssertionFunc + expectedErrorMSG string + testingFunc require.ComparisonAssertionFunc + expectedOut TokenInfo + expectedMSG string + expectPayload TokenRequestPayload + } + + defaultInfo := DeviceAuthInfo{ + DeviceCode: "test", + ExpiresIn: 10, + Interval: 1, + } + + tokenReqPayload := TokenRequestPayload{ + GrantType: auth0RefreshGrant, + ClientID: "test", + RefreshToken: "refresh_test", + } + + testCase1 := test{ + name: "Payload Is Valid", + inputInfo: defaultInfo, + inputReqCode: 200, + testingErrFunc: require.Error, + testingFunc: require.EqualValues, + expectPayload: tokenReqPayload, + } + + testCase2 := test{ + name: "Exit On Network Error", + inputInfo: defaultInfo, + expectPayload: tokenReqPayload, + inputReqError: fmt.Errorf("error"), + testingErrFunc: require.Error, + expectedErrorMSG: "should return error", + testingFunc: require.EqualValues, + } + + testCase3 := test{ + name: "Exit On Non 200 Status Code", + inputInfo: defaultInfo, + inputReqCode: 401, + expectPayload: tokenReqPayload, + testingErrFunc: require.Error, + expectedErrorMSG: "should return error", + testingFunc: require.EqualValues, + } + + audience := "test" + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{"aud": audience}) + var hmacSampleSecret []byte + tokenString, _ := token.SignedString(hmacSampleSecret) + + testCase4 := test{ + name: "Exit On Invalid Audience", + inputInfo: defaultInfo, + inputResBody: fmt.Sprintf("{\"access_token\":\"%s\"}", tokenString), + inputReqCode: 200, + inputAudience: "super test", + testingErrFunc: require.Error, + testingFunc: require.EqualValues, + expectPayload: tokenReqPayload, + } + + testCase5 := test{ + name: "Received Token Info", + inputInfo: defaultInfo, + inputResBody: fmt.Sprintf("{\"access_token\":\"%s\"}", tokenString), + inputReqCode: 200, + inputAudience: audience, + testingErrFunc: require.NoError, + testingFunc: require.EqualValues, + expectPayload: tokenReqPayload, + expectedOut: TokenInfo{AccessToken: tokenString}, + } + + for _, testCase := range []test{testCase1, testCase2, testCase3, testCase4, testCase5} { + t.Run(testCase.name, func(t *testing.T) { + + httpClient := mockHTTPClient{ + resBody: testCase.inputResBody, + code: testCase.inputReqCode, + err: testCase.inputReqError, + MaxReqs: testCase.inputMaxReqs, + } + + auth0 := Auth0{ + Audience: testCase.inputAudience, + ClientID: testCase.expectPayload.ClientID, + Domain: "test.auth0.com", + HTTPClient: &httpClient, + } + + tokenInfo, err := auth0.RotateAccessToken(context.TODO(), testCase.expectPayload.RefreshToken) + testCase.testingErrFunc(t, err, testCase.expectedErrorMSG) + + var payload []byte + var emptyPayload TokenRequestPayload + if testCase.expectPayload != emptyPayload { + payload, _ = json.Marshal(testCase.expectPayload) + } + require.EqualValues(t, string(payload), httpClient.reqBody, "payload should match") + + testCase.testingFunc(t, testCase.expectedOut, tokenInfo, testCase.expectedMSG) + + }) + } +} diff --git a/client/internal/oauth/oauth.go b/client/internal/oauth/oauth.go index 07e6005f7..5fe9a9a96 100644 --- a/client/internal/oauth/oauth.go +++ b/client/internal/oauth/oauth.go @@ -32,5 +32,6 @@ type TokenInfo struct { // Client is a OAuth client interface for various idp providers type Client interface { RequestDeviceCode(ctx context.Context) (DeviceAuthInfo, error) + RotateAccessToken(ctx context.Context, refreshToken string) (TokenInfo, error) WaitToken(ctx context.Context, info DeviceAuthInfo) (TokenInfo, error) } diff --git a/client/internal/oauth/secret.go b/client/internal/oauth/secret.go new file mode 100644 index 000000000..c4a7a5cdb --- /dev/null +++ b/client/internal/oauth/secret.go @@ -0,0 +1,66 @@ +package oauth + +import ( + "fmt" + "github.com/99designs/keyring" +) + +// ServiceName default service name for saving the secret +const ServiceName = "Wiretrustee" + +func newSecretAPI() (keyring.Keyring, error) { + return keyring.Open(keyring.Config{ + ServiceName: ServiceName, + //KeychainName: ServiceName, + }) +} + +// SetSecret stores the secret in the system's available backend +func SetSecret(key string, value string) error { + storeAPI, err := newSecretAPI() + if err != nil { + return fmt.Errorf("failed to create secret API for setting a secret, error: %v", err) + } + + item := keyring.Item{ + Key: key, + Data: []byte(value), + } + + err = storeAPI.Set(item) + if err != nil { + return fmt.Errorf("failed to set the secret, error: %v", err) + } + + return nil +} + +// GetSecret retrieves a secret from the system's available backend +func GetSecret(key string) (string, error) { + storeAPI, err := newSecretAPI() + if err != nil { + return "", fmt.Errorf("failed to create secret API for getting a secret, error: %v", err) + } + + item, err := storeAPI.Get(key) + if err != nil { + return "", fmt.Errorf("failed to get secret, error: %v", err) + } + + return string(item.Data), nil +} + +// DeleteSecret deletes a secret from the system's available backend +func DeleteSecret(key string) error { + storeAPI, err := newSecretAPI() + if err != nil { + return fmt.Errorf("failed to create secret API for deleting a secret, error: %v", err) + } + + err = storeAPI.Remove(key) + if err != nil { + return fmt.Errorf("failed to delete secret, error: %v", err) + } + + return nil +} diff --git a/client/internal/oauth/secret_test.go b/client/internal/oauth/secret_test.go new file mode 100644 index 000000000..fedbee13c --- /dev/null +++ b/client/internal/oauth/secret_test.go @@ -0,0 +1,27 @@ +package oauth + +import ( + "github.com/stretchr/testify/require" + "os" + "testing" +) + +func TestSecret(t *testing.T) { + // this test is not ready to run as part of our ci/cd + // todo fix testing + if os.Getenv("CI") == "true" { + t.Skip("skipping testing in github actions") + } + + key := "testing" + value := "1234" + err := SetSecret(key, value) + require.NoError(t, err, "should set secret") + + v, err := GetSecret(key) + require.NoError(t, err, "should retrieve secret") + require.Equal(t, value, v, "values should match") + + err = DeleteSecret(key) + require.NoError(t, err, "should delete secret") +} diff --git a/go.mod b/go.mod index 4d3210f78..303753083 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/vishvananda/netlink v1.1.0 golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 - golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e + golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a golang.zx2c4.com/wireguard v0.0.0-20211209221555-9c9e7e272434 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de golang.zx2c4.com/wireguard/windows v0.5.1 @@ -28,6 +28,7 @@ require ( ) require ( + github.com/99designs/keyring v1.2.1 github.com/getlantern/systray v1.2.1 github.com/magiconair/properties v1.8.5 github.com/rs/xid v1.3.0 @@ -36,8 +37,11 @@ require ( ) require ( + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/BurntSushi/toml v0.4.1 // indirect + github.com/danieljoos/wincred v1.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 // indirect @@ -46,12 +50,15 @@ require ( github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect github.com/go-stack/stack v1.8.0 // indirect + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/google/go-cmp v0.5.6 // indirect + github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect github.com/mdlayher/genetlink v1.1.0 // indirect github.com/mdlayher/netlink v1.4.2 // indirect github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb // indirect + github.com/mtibben/percent v0.2.1 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect github.com/pion/dtls/v2 v2.1.2 // indirect @@ -66,6 +73,7 @@ require ( github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect golang.org/x/mod v0.5.1 // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 // indirect golang.org/x/tools v0.1.8 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect diff --git a/go.sum b/go.sum index 5a007a7f8..d3e02498c 100644 --- a/go.sum +++ b/go.sum @@ -46,6 +46,10 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl 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/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o= +github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -94,9 +98,13 @@ github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWH 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.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= +github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= 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/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM= +github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= 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= @@ -145,6 +153,8 @@ github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEK github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -228,6 +238,8 @@ github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0 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/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= 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= @@ -293,7 +305,6 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/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= @@ -355,7 +366,11 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -437,6 +452,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= +github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -696,6 +713,7 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/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-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -708,9 +726,11 @@ golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a h1:ppl5mZgokTT8uPkmYOyEUmPTr3ypaKkg5eFOGrAmxxE= +golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/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 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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= @@ -951,8 +971,9 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=