From c9d1f92bc71eb4e3d0ab9d6b5bef2d3ed29301e9 Mon Sep 17 00:00:00 2001 From: "Jorge L. Fatta" Date: Fri, 5 Mar 2021 22:55:47 -0300 Subject: [PATCH] feat: store and use refresh token (#141) * feat: save refresh token into kc * WIP * wip * feat: refresh token * cleanup parse payload * cleanup tenant * fix login vs refresh flow * document refresh * docs: auth readme * Update README.md --- go.mod | 1 + go.sum | 7 + internal/auth/README.md | 10 + internal/auth/auth.go | 21 + internal/auth/secrets.go | 15 + internal/auth/token.go | 60 ++ internal/cli/cli.go | 30 +- internal/cli/login.go | 5 +- .../danieljoos/wincred/.gitattributes | 1 + .../github.com/danieljoos/wincred/.gitignore | 23 + vendor/github.com/danieljoos/wincred/LICENSE | 21 + .../github.com/danieljoos/wincred/README.md | 98 ++ .../danieljoos/wincred/conversion.go | 131 +++ .../wincred/conversion_unsupported.go | 11 + vendor/github.com/danieljoos/wincred/go.mod | 5 + vendor/github.com/danieljoos/wincred/go.sum | 12 + vendor/github.com/danieljoos/wincred/sys.go | 143 +++ .../danieljoos/wincred/sys_unsupported.go | 36 + vendor/github.com/danieljoos/wincred/types.go | 69 ++ .../github.com/danieljoos/wincred/wincred.go | 111 +++ vendor/github.com/godbus/dbus/v5/.travis.yml | 50 + .../github.com/godbus/dbus/v5/CONTRIBUTING.md | 50 + vendor/github.com/godbus/dbus/v5/LICENSE | 25 + vendor/github.com/godbus/dbus/v5/MAINTAINERS | 3 + .../github.com/godbus/dbus/v5/README.markdown | 44 + vendor/github.com/godbus/dbus/v5/auth.go | 252 +++++ .../godbus/dbus/v5/auth_anonymous.go | 16 + .../godbus/dbus/v5/auth_external.go | 26 + vendor/github.com/godbus/dbus/v5/auth_sha1.go | 102 ++ vendor/github.com/godbus/dbus/v5/call.go | 60 ++ vendor/github.com/godbus/dbus/v5/conn.go | 912 ++++++++++++++++++ .../github.com/godbus/dbus/v5/conn_darwin.go | 37 + .../github.com/godbus/dbus/v5/conn_other.go | 93 ++ vendor/github.com/godbus/dbus/v5/conn_unix.go | 17 + .../github.com/godbus/dbus/v5/conn_windows.go | 15 + vendor/github.com/godbus/dbus/v5/dbus.go | 428 ++++++++ vendor/github.com/godbus/dbus/v5/decoder.go | 286 ++++++ .../godbus/dbus/v5/default_handler.go | 328 +++++++ vendor/github.com/godbus/dbus/v5/doc.go | 69 ++ vendor/github.com/godbus/dbus/v5/encoder.go | 210 ++++ vendor/github.com/godbus/dbus/v5/export.go | 412 ++++++++ vendor/github.com/godbus/dbus/v5/go.mod | 3 + vendor/github.com/godbus/dbus/v5/go.sum | 0 vendor/github.com/godbus/dbus/v5/homedir.go | 28 + .../godbus/dbus/v5/homedir_dynamic.go | 15 + .../godbus/dbus/v5/homedir_static.go | 45 + vendor/github.com/godbus/dbus/v5/match.go | 62 ++ vendor/github.com/godbus/dbus/v5/message.go | 353 +++++++ vendor/github.com/godbus/dbus/v5/object.go | 211 ++++ .../godbus/dbus/v5/server_interfaces.go | 107 ++ vendor/github.com/godbus/dbus/v5/sig.go | 259 +++++ .../godbus/dbus/v5/transport_darwin.go | 6 + .../godbus/dbus/v5/transport_generic.go | 50 + .../godbus/dbus/v5/transport_nonce_tcp.go | 39 + .../godbus/dbus/v5/transport_tcp.go | 41 + .../godbus/dbus/v5/transport_unix.go | 214 ++++ .../dbus/v5/transport_unixcred_dragonfly.go | 95 ++ .../dbus/v5/transport_unixcred_freebsd.go | 91 ++ .../dbus/v5/transport_unixcred_linux.go | 25 + .../dbus/v5/transport_unixcred_openbsd.go | 14 + vendor/github.com/godbus/dbus/v5/variant.go | 144 +++ .../godbus/dbus/v5/variant_lexer.go | 284 ++++++ .../godbus/dbus/v5/variant_parser.go | 817 ++++++++++++++++ .../zalando/go-keyring/.catwatch.yml | 1 + .../github.com/zalando/go-keyring/.zappr.yml | 8 + .../zalando/go-keyring/CONTRIBUTING.md | 12 + vendor/github.com/zalando/go-keyring/LICENSE | 21 + .../github.com/zalando/go-keyring/MAINTAINERS | 2 + .../github.com/zalando/go-keyring/README.md | 146 +++ .../github.com/zalando/go-keyring/SECURITY.md | 8 + .../zalando/go-keyring/appveyor.yml | 11 + vendor/github.com/zalando/go-keyring/go.mod | 8 + vendor/github.com/zalando/go-keyring/go.sum | 16 + .../github.com/zalando/go-keyring/keyring.go | 38 + .../zalando/go-keyring/keyring_darwin.go | 94 ++ .../zalando/go-keyring/keyring_fallback.go | 23 + .../zalando/go-keyring/keyring_linux.go | 120 +++ .../zalando/go-keyring/keyring_mock.go | 46 + .../zalando/go-keyring/keyring_windows.go | 52 + .../secret_service/secret_service.go | 245 +++++ vendor/modules.txt | 8 + 81 files changed, 8000 insertions(+), 7 deletions(-) create mode 100644 internal/auth/README.md create mode 100644 internal/auth/secrets.go create mode 100644 internal/auth/token.go create mode 100644 vendor/github.com/danieljoos/wincred/.gitattributes create mode 100644 vendor/github.com/danieljoos/wincred/.gitignore create mode 100644 vendor/github.com/danieljoos/wincred/LICENSE create mode 100644 vendor/github.com/danieljoos/wincred/README.md create mode 100644 vendor/github.com/danieljoos/wincred/conversion.go create mode 100644 vendor/github.com/danieljoos/wincred/conversion_unsupported.go create mode 100644 vendor/github.com/danieljoos/wincred/go.mod create mode 100644 vendor/github.com/danieljoos/wincred/go.sum create mode 100644 vendor/github.com/danieljoos/wincred/sys.go create mode 100644 vendor/github.com/danieljoos/wincred/sys_unsupported.go create mode 100644 vendor/github.com/danieljoos/wincred/types.go create mode 100644 vendor/github.com/danieljoos/wincred/wincred.go create mode 100644 vendor/github.com/godbus/dbus/v5/.travis.yml create mode 100644 vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md create mode 100644 vendor/github.com/godbus/dbus/v5/LICENSE create mode 100644 vendor/github.com/godbus/dbus/v5/MAINTAINERS create mode 100644 vendor/github.com/godbus/dbus/v5/README.markdown create mode 100644 vendor/github.com/godbus/dbus/v5/auth.go create mode 100644 vendor/github.com/godbus/dbus/v5/auth_anonymous.go create mode 100644 vendor/github.com/godbus/dbus/v5/auth_external.go create mode 100644 vendor/github.com/godbus/dbus/v5/auth_sha1.go create mode 100644 vendor/github.com/godbus/dbus/v5/call.go create mode 100644 vendor/github.com/godbus/dbus/v5/conn.go create mode 100644 vendor/github.com/godbus/dbus/v5/conn_darwin.go create mode 100644 vendor/github.com/godbus/dbus/v5/conn_other.go create mode 100644 vendor/github.com/godbus/dbus/v5/conn_unix.go create mode 100644 vendor/github.com/godbus/dbus/v5/conn_windows.go create mode 100644 vendor/github.com/godbus/dbus/v5/dbus.go create mode 100644 vendor/github.com/godbus/dbus/v5/decoder.go create mode 100644 vendor/github.com/godbus/dbus/v5/default_handler.go create mode 100644 vendor/github.com/godbus/dbus/v5/doc.go create mode 100644 vendor/github.com/godbus/dbus/v5/encoder.go create mode 100644 vendor/github.com/godbus/dbus/v5/export.go create mode 100644 vendor/github.com/godbus/dbus/v5/go.mod create mode 100644 vendor/github.com/godbus/dbus/v5/go.sum create mode 100644 vendor/github.com/godbus/dbus/v5/homedir.go create mode 100644 vendor/github.com/godbus/dbus/v5/homedir_dynamic.go create mode 100644 vendor/github.com/godbus/dbus/v5/homedir_static.go create mode 100644 vendor/github.com/godbus/dbus/v5/match.go create mode 100644 vendor/github.com/godbus/dbus/v5/message.go create mode 100644 vendor/github.com/godbus/dbus/v5/object.go create mode 100644 vendor/github.com/godbus/dbus/v5/server_interfaces.go create mode 100644 vendor/github.com/godbus/dbus/v5/sig.go create mode 100644 vendor/github.com/godbus/dbus/v5/transport_darwin.go create mode 100644 vendor/github.com/godbus/dbus/v5/transport_generic.go create mode 100644 vendor/github.com/godbus/dbus/v5/transport_nonce_tcp.go create mode 100644 vendor/github.com/godbus/dbus/v5/transport_tcp.go create mode 100644 vendor/github.com/godbus/dbus/v5/transport_unix.go create mode 100644 vendor/github.com/godbus/dbus/v5/transport_unixcred_dragonfly.go create mode 100644 vendor/github.com/godbus/dbus/v5/transport_unixcred_freebsd.go create mode 100644 vendor/github.com/godbus/dbus/v5/transport_unixcred_linux.go create mode 100644 vendor/github.com/godbus/dbus/v5/transport_unixcred_openbsd.go create mode 100644 vendor/github.com/godbus/dbus/v5/variant.go create mode 100644 vendor/github.com/godbus/dbus/v5/variant_lexer.go create mode 100644 vendor/github.com/godbus/dbus/v5/variant_parser.go create mode 100644 vendor/github.com/zalando/go-keyring/.catwatch.yml create mode 100644 vendor/github.com/zalando/go-keyring/.zappr.yml create mode 100644 vendor/github.com/zalando/go-keyring/CONTRIBUTING.md create mode 100644 vendor/github.com/zalando/go-keyring/LICENSE create mode 100644 vendor/github.com/zalando/go-keyring/MAINTAINERS create mode 100644 vendor/github.com/zalando/go-keyring/README.md create mode 100644 vendor/github.com/zalando/go-keyring/SECURITY.md create mode 100644 vendor/github.com/zalando/go-keyring/appveyor.yml create mode 100644 vendor/github.com/zalando/go-keyring/go.mod create mode 100644 vendor/github.com/zalando/go-keyring/go.sum create mode 100644 vendor/github.com/zalando/go-keyring/keyring.go create mode 100644 vendor/github.com/zalando/go-keyring/keyring_darwin.go create mode 100644 vendor/github.com/zalando/go-keyring/keyring_fallback.go create mode 100644 vendor/github.com/zalando/go-keyring/keyring_linux.go create mode 100644 vendor/github.com/zalando/go-keyring/keyring_mock.go create mode 100644 vendor/github.com/zalando/go-keyring/keyring_windows.go create mode 100644 vendor/github.com/zalando/go-keyring/secret_service/secret_service.go diff --git a/go.mod b/go.mod index bfa643cbe..8f314378d 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/stretchr/testify v1.7.0 github.com/tidwall/pretty v1.1.0 github.com/ulikunitz/xz v0.5.10 // indirect + github.com/zalando/go-keyring v0.1.1 golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect golang.org/x/sys v0.0.0-20210305023407-0d6cb8bd5a4b // indirect golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d diff --git a/go.sum b/go.sum index cb210e829..a362e41d8 100644 --- a/go.sum +++ b/go.sum @@ -73,6 +73,8 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/danieljoos/wincred v1.1.0 h1:3RNcEpBg4IhIChZdFRSdlQt1QjCp1sMAPIrOnm7Yf8g= +github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= 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= @@ -106,6 +108,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/goccy/go-json v0.4.7 h1:xGUjaNfhpqhKAV2LoyNXihFLZ8ABSST8B+W+duHqkPI= github.com/goccy/go-json v0.4.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -318,6 +322,7 @@ 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.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -342,6 +347,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zalando/go-keyring v0.1.1 h1:w2V9lcx/Uj4l+dzAf1m9s+DJ1O8ROkEHnynonHjTcYE= +github.com/zalando/go-keyring v0.1.1/go.mod h1:OIC+OZ28XbmwFxU/Rp9V7eKzZjamBJwRzC8UFJH9+L8= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= diff --git a/internal/auth/README.md b/internal/auth/README.md new file mode 100644 index 000000000..9fbcc1185 --- /dev/null +++ b/internal/auth/README.md @@ -0,0 +1,10 @@ +# Auth package + +The CLI authentication follows this approach: + +1. `$ auth0 login` uses **Auth0 Device Flow** to get an `acccess token` and a `refresh token` for the selected tenant. +1. The access token is stored at the configuration file. +1. The refresh token is stored at the OS keychain (supports macOS, Linux, and Windows thanks to https://github.com/zalando/go-keyring). +1. During regular commands initialization, the access token is used to instantiate an Auth0 API client. + - If the token is expired according to the value stored on the configuration file, a new one is requested using the refresh token. + - In case of any error, the interactive login flow is triggered. diff --git a/internal/auth/auth.go b/internal/auth/auth.go index c8b58f2c4..4877c550a 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -17,17 +17,29 @@ const ( deviceCodeEndpoint = "https://auth0.auth0.com/oauth/device/code" oauthTokenEndpoint = "https://auth0.auth0.com/oauth/token" audiencePath = "/api/v2/" + + secretsNamespace = "auth0-cli" ) var requiredScopes = []string{ "openid", + "offline_access", // <-- to get a refresh token. "create:actions", "delete:actions", "read:actions", "update:actions", "create:clients", "delete:clients", "read:clients", "update:clients", "create:resource_servers", "delete:resource_servers", "read:resource_servers", "update:resource_servers", "read:client_keys", "read:logs", } +// SecretStore provides secure storage for sensitive data +type SecretStore interface { + // Set sets the secret + Set(namespace, key, value string) error + // Get gets the secret + Get(namespace, key string) (string, error) +} + type Authenticator struct { + Secrets SecretStore } type Result struct { @@ -82,6 +94,7 @@ func (a *Authenticator) Wait(ctx context.Context, state State) (Result, error) { var res struct { AccessToken string `json:"access_token"` IDToken string `json:"id_token"` + RefreshToken string `json:"refresh_token"` Scope string `json:"scope"` ExpiresIn int64 `json:"expires_in"` TokenType string `json:"token_type"` @@ -105,6 +118,13 @@ func (a *Authenticator) Wait(ctx context.Context, state State) (Result, error) { if err != nil { return Result{}, fmt.Errorf("cannot parse tenant from the given access token: %w", err) } + + // store the refresh token + err = a.Secrets.Set(secretsNamespace, ten, res.RefreshToken) + if err != nil { + return Result{}, fmt.Errorf("cannot store refresh token: %w", err) + } + return Result{ AccessToken: res.AccessToken, ExpiresIn: res.ExpiresIn, @@ -147,6 +167,7 @@ func parseTenant(accessToken string) (tenant, domain string, err error) { if err := json.Unmarshal([]byte(v), &payload); err != nil { return "", "", err } + for _, aud := range payload.AUDs { u, err := url.Parse(aud) if err != nil { diff --git a/internal/auth/secrets.go b/internal/auth/secrets.go new file mode 100644 index 000000000..06f76820e --- /dev/null +++ b/internal/auth/secrets.go @@ -0,0 +1,15 @@ +package auth + +import "github.com/zalando/go-keyring" + +type Keyring struct{} + +// Set sets the given key/value pair with the given namespace. +func (k *Keyring) Set(namespace, key, value string) error { + return keyring.Set(namespace, key, value) +} + +// Get gets a value for the given namespace and key. +func (k *Keyring) Get(namespace, key string) (string, error) { + return keyring.Get(namespace, key) +} diff --git a/internal/auth/token.go b/internal/auth/token.go new file mode 100644 index 000000000..7fee8d290 --- /dev/null +++ b/internal/auth/token.go @@ -0,0 +1,60 @@ +package auth + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "net/url" +) + +type TokenResponse struct { + AccessToken string `json:"access_token"` + IDToken string `json:"id_token"` + TokenType string `json:"token_type"` + ExpiresIn int `json:"expires_in"` +} + +type TokenRetriever struct { + Secrets SecretStore + Client *http.Client +} + +// Refresh gets a new access token from the provided refresh token, +// The request is used the default client_id and endpoint for device authentication. +func (t *TokenRetriever) Refresh(ctx context.Context, tenant string) (TokenResponse, error) { + // get stored refresh token: + refreshToken, err := t.Secrets.Get(secretsNamespace, tenant) + if err != nil { + return TokenResponse{}, fmt.Errorf("cannot get the stored refresh token: %w", err) + } + if refreshToken == "" { + return TokenResponse{}, errors.New("cannot use the stored refresh token: the token is empty") + } + // get access token: + r, err := t.Client.PostForm(oauthTokenEndpoint, url.Values{ + "grant_type": {"refresh_token"}, + "client_id": {clientID}, + "refresh_token": {refreshToken}, + }) + if err != nil { + return TokenResponse{}, fmt.Errorf("cannot get a new access token from the refresh token: %w", err) + } + + defer r.Body.Close() + if r.StatusCode != http.StatusOK { + b, _ := ioutil.ReadAll(r.Body) + bodyStr := string(b) + return TokenResponse{}, fmt.Errorf("cannot get a new access token from the refresh token: %s", bodyStr) + } + + var res TokenResponse + err = json.NewDecoder(r.Body).Decode(&res) + if err != nil { + return TokenResponse{}, fmt.Errorf("cannot decode response: %w", err) + } + + return res, nil +} diff --git a/internal/cli/cli.go b/internal/cli/cli.go index af9149112..5705fcefb 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io/ioutil" + "net/http" "os" "path" "path/filepath" @@ -14,6 +15,7 @@ import ( "time" "github.com/auth0/auth0-cli/internal/ansi" + "github.com/auth0/auth0-cli/internal/auth" "github.com/auth0/auth0-cli/internal/auth0" "github.com/auth0/auth0-cli/internal/display" "github.com/lestrrat-go/jwx/jwt" @@ -118,15 +120,35 @@ func (c *cli) setup(ctx context.Context) error { if t.AccessToken == "" { return errUnauthenticated - } // check if the stored access token is expired: if isExpired(t.ExpiresAt, accessTokenExpThreshold) { - // ask and guide the user through the login process: - err := RunLogin(ctx, c, true) + // use the refresh token to get a new access token: + tr := &auth.TokenRetriever{ + Secrets: &auth.Keyring{}, + Client: http.DefaultClient, + } + + res, err := tr.Refresh(ctx, t.Name) if err != nil { - return err + // ask and guide the user through the login process: + c.renderer.Errorf("failed to renew access token, %s", err) + err = RunLogin(ctx, c, true) + if err != nil { + return err + } + } else { + // persist the updated tenant with renewed access token + t.AccessToken = res.AccessToken + t.ExpiresAt = time.Now().Add( + time.Duration(res.ExpiresIn) * time.Second, + ) + + err = c.addTenant(t) + if err != nil { + return err + } } } diff --git a/internal/cli/login.go b/internal/cli/login.go index cfad76830..3102e6c48 100644 --- a/internal/cli/login.go +++ b/internal/cli/login.go @@ -31,14 +31,14 @@ func loginCmd(cli *cli) *cobra.Command { // this will only affect the messages. func RunLogin(ctx context.Context, cli *cli, expired bool) error { if expired { - cli.renderer.Warnf("Your session expired. Please sign in to re-authorize the CLI.") + cli.renderer.Warnf("Please sign in to re-authorize the CLI.") } else { cli.renderer.Heading("✪ Welcome to the Auth0 CLI 🎊.") cli.renderer.Infof("To set it up, you will need to sign in to your Auth0 account and authorize the CLI to access the API.") cli.renderer.Infof("If you don't have an account, please go to https://auth0.com/signup, otherwise continue in the browser.\n\n") } - a := &auth.Authenticator{} + a := &auth.Authenticator{Secrets: &auth.Keyring{}} state, err := a.Start(ctx) if err != nil { return fmt.Errorf("could not start the authentication process: %w.", err) @@ -70,5 +70,4 @@ func RunLogin(ctx context.Context, cli *cli, expired bool) error { time.Duration(res.ExpiresIn) * time.Second, ), }) - } diff --git a/vendor/github.com/danieljoos/wincred/.gitattributes b/vendor/github.com/danieljoos/wincred/.gitattributes new file mode 100644 index 000000000..d207b1802 --- /dev/null +++ b/vendor/github.com/danieljoos/wincred/.gitattributes @@ -0,0 +1 @@ +*.go text eol=lf diff --git a/vendor/github.com/danieljoos/wincred/.gitignore b/vendor/github.com/danieljoos/wincred/.gitignore new file mode 100644 index 000000000..836562412 --- /dev/null +++ b/vendor/github.com/danieljoos/wincred/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/vendor/github.com/danieljoos/wincred/LICENSE b/vendor/github.com/danieljoos/wincred/LICENSE new file mode 100644 index 000000000..2f436f1b3 --- /dev/null +++ b/vendor/github.com/danieljoos/wincred/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Daniel Joos + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/danieljoos/wincred/README.md b/vendor/github.com/danieljoos/wincred/README.md new file mode 100644 index 000000000..064efb763 --- /dev/null +++ b/vendor/github.com/danieljoos/wincred/README.md @@ -0,0 +1,98 @@ +wincred +======= + +Go wrapper around the Windows Credential Manager API functions. + +![Go](https://github.com/danieljoos/wincred/workflows/Go/badge.svg) +[![GoDoc](https://godoc.org/github.com/danieljoos/wincred?status.svg)](https://godoc.org/github.com/danieljoos/wincred) + + +Installation +------------ + +```Go +go get github.com/danieljoos/wincred +``` + + +Usage +----- + +See the following examples: + +### Create and store a new generic credential object +```Go +package main + +import ( + "fmt" + "github.com/danieljoos/wincred" +) + +func main() { + cred := wincred.NewGenericCredential("myGoApplication") + cred.CredentialBlob = []byte("my secret") + err := cred.Write() + + if err != nil { + fmt.Println(err) + } +} +``` + +### Retrieve a credential object +```Go +package main + +import ( + "fmt" + "github.com/danieljoos/wincred" +) + +func main() { + cred, err := wincred.GetGenericCredential("myGoApplication") + if err == nil { + fmt.Println(string(cred.CredentialBlob)) + } +} +``` + +### Remove a credential object +```Go +package main + +import ( + "fmt" + "github.com/danieljoos/wincred" +) + +func main() { + cred, err := wincred.GetGenericCredential("myGoApplication") + if err != nil { + fmt.Println(err) + return + } + cred.Delete() +} +``` + +### List all available credentials +```Go +package main + +import ( + "fmt" + "github.com/danieljoos/wincred" +) + +func main() { + creds, err := wincred.List() + if err != nil { + fmt.Println(err) + return + } + for i := range(creds) { + fmt.Println(creds[i].TargetName) + } +} +``` diff --git a/vendor/github.com/danieljoos/wincred/conversion.go b/vendor/github.com/danieljoos/wincred/conversion.go new file mode 100644 index 000000000..685f90a8f --- /dev/null +++ b/vendor/github.com/danieljoos/wincred/conversion.go @@ -0,0 +1,131 @@ +// +build windows + +package wincred + +import ( + "encoding/binary" + "reflect" + "syscall" + "time" + "unicode/utf16" + "unsafe" +) + +// uf16PtrToString creates a Go string from a pointer to a UTF16 encoded zero-terminated string. +// Such pointers are returned from the Windows API calls. +// The function creates a copy of the string. +func utf16PtrToString(wstr *uint16) string { + if wstr != nil { + for len := 0; ; len++ { + ptr := unsafe.Pointer(uintptr(unsafe.Pointer(wstr)) + uintptr(len)*unsafe.Sizeof(*wstr)) // see https://golang.org/pkg/unsafe/#Pointer (3) + if *(*uint16)(ptr) == 0 { + return string(utf16.Decode(*(*[]uint16)(unsafe.Pointer(&reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(wstr)), + Len: len, + Cap: len, + })))) + } + } + } + return "" +} + +// utf16ToByte creates a byte array from a given UTF 16 char array. +func utf16ToByte(wstr []uint16) (result []byte) { + result = make([]byte, len(wstr)*2) + for i := range wstr { + binary.LittleEndian.PutUint16(result[(i*2):(i*2)+2], wstr[i]) + } + return +} + +// utf16FromString creates a UTF16 char array from a string. +func utf16FromString(str string) []uint16 { + return syscall.StringToUTF16(str) +} + +// goBytes copies the given C byte array to a Go byte array (see `C.GoBytes`). +// This function avoids having cgo as dependency. +func goBytes(src uintptr, len uint32) []byte { + if src == uintptr(0) { + return []byte{} + } + rv := make([]byte, len) + copy(rv, *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ + Data: src, + Len: int(len), + Cap: int(len), + }))) + return rv +} + +// Convert the given CREDENTIAL struct to a more usable structure +func sysToCredential(cred *sysCREDENTIAL) (result *Credential) { + if cred == nil { + return nil + } + result = new(Credential) + result.Comment = utf16PtrToString(cred.Comment) + result.TargetName = utf16PtrToString(cred.TargetName) + result.TargetAlias = utf16PtrToString(cred.TargetAlias) + result.UserName = utf16PtrToString(cred.UserName) + result.LastWritten = time.Unix(0, cred.LastWritten.Nanoseconds()) + result.Persist = CredentialPersistence(cred.Persist) + result.CredentialBlob = goBytes(cred.CredentialBlob, cred.CredentialBlobSize) + result.Attributes = make([]CredentialAttribute, cred.AttributeCount) + attrSlice := *(*[]sysCREDENTIAL_ATTRIBUTE)(unsafe.Pointer(&reflect.SliceHeader{ + Data: cred.Attributes, + Len: int(cred.AttributeCount), + Cap: int(cred.AttributeCount), + })) + for i, attr := range attrSlice { + resultAttr := &result.Attributes[i] + resultAttr.Keyword = utf16PtrToString(attr.Keyword) + resultAttr.Value = goBytes(attr.Value, attr.ValueSize) + } + return result +} + +// Convert the given Credential object back to a CREDENTIAL struct, which can be used for calling the +// Windows APIs +func sysFromCredential(cred *Credential) (result *sysCREDENTIAL) { + if cred == nil { + return nil + } + result = new(sysCREDENTIAL) + result.Flags = 0 + result.Type = 0 + result.TargetName, _ = syscall.UTF16PtrFromString(cred.TargetName) + result.Comment, _ = syscall.UTF16PtrFromString(cred.Comment) + result.LastWritten = syscall.NsecToFiletime(cred.LastWritten.UnixNano()) + result.CredentialBlobSize = uint32(len(cred.CredentialBlob)) + if len(cred.CredentialBlob) > 0 { + result.CredentialBlob = uintptr(unsafe.Pointer(&cred.CredentialBlob[0])) + } else { + result.CredentialBlob = 0 + } + result.Persist = uint32(cred.Persist) + result.AttributeCount = uint32(len(cred.Attributes)) + attributes := make([]sysCREDENTIAL_ATTRIBUTE, len(cred.Attributes)) + if len(attributes) > 0 { + result.Attributes = uintptr(unsafe.Pointer(&attributes[0])) + } else { + result.Attributes = 0 + } + for i := range cred.Attributes { + inAttr := &cred.Attributes[i] + outAttr := &attributes[i] + outAttr.Keyword, _ = syscall.UTF16PtrFromString(inAttr.Keyword) + outAttr.Flags = 0 + outAttr.ValueSize = uint32(len(inAttr.Value)) + if len(inAttr.Value) > 0 { + outAttr.Value = uintptr(unsafe.Pointer(&inAttr.Value[0])) + } else { + outAttr.Value = 0 + } + } + result.TargetAlias, _ = syscall.UTF16PtrFromString(cred.TargetAlias) + result.UserName, _ = syscall.UTF16PtrFromString(cred.UserName) + + return +} diff --git a/vendor/github.com/danieljoos/wincred/conversion_unsupported.go b/vendor/github.com/danieljoos/wincred/conversion_unsupported.go new file mode 100644 index 000000000..a1ea72075 --- /dev/null +++ b/vendor/github.com/danieljoos/wincred/conversion_unsupported.go @@ -0,0 +1,11 @@ +// +build !windows + +package wincred + +func utf16ToByte(...interface{}) []byte { + return nil +} + +func utf16FromString(...interface{}) []uint16 { + return nil +} diff --git a/vendor/github.com/danieljoos/wincred/go.mod b/vendor/github.com/danieljoos/wincred/go.mod new file mode 100644 index 000000000..b56158042 --- /dev/null +++ b/vendor/github.com/danieljoos/wincred/go.mod @@ -0,0 +1,5 @@ +module github.com/danieljoos/wincred + +go 1.13 + +require github.com/stretchr/testify v1.5.1 diff --git a/vendor/github.com/danieljoos/wincred/go.sum b/vendor/github.com/danieljoos/wincred/go.sum new file mode 100644 index 000000000..c0565d71d --- /dev/null +++ b/vendor/github.com/danieljoos/wincred/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/danieljoos/wincred/sys.go b/vendor/github.com/danieljoos/wincred/sys.go new file mode 100644 index 000000000..7b83e8438 --- /dev/null +++ b/vendor/github.com/danieljoos/wincred/sys.go @@ -0,0 +1,143 @@ +// +build windows + +package wincred + +import ( + "reflect" + "syscall" + "unsafe" +) + +var ( + modadvapi32 = syscall.NewLazyDLL("advapi32.dll") + + procCredRead proc = modadvapi32.NewProc("CredReadW") + procCredWrite proc = modadvapi32.NewProc("CredWriteW") + procCredDelete proc = modadvapi32.NewProc("CredDeleteW") + procCredFree proc = modadvapi32.NewProc("CredFree") + procCredEnumerate proc = modadvapi32.NewProc("CredEnumerateW") +) + +// Interface for syscall.Proc: helps testing +type proc interface { + Call(a ...uintptr) (r1, r2 uintptr, lastErr error) +} + +// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw +type sysCREDENTIAL struct { + Flags uint32 + Type uint32 + TargetName *uint16 + Comment *uint16 + LastWritten syscall.Filetime + CredentialBlobSize uint32 + CredentialBlob uintptr + Persist uint32 + AttributeCount uint32 + Attributes uintptr + TargetAlias *uint16 + UserName *uint16 +} + +// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credential_attributew +type sysCREDENTIAL_ATTRIBUTE struct { + Keyword *uint16 + Flags uint32 + ValueSize uint32 + Value uintptr +} + +// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw +type sysCRED_TYPE uint32 + +const ( + sysCRED_TYPE_GENERIC sysCRED_TYPE = 0x1 + sysCRED_TYPE_DOMAIN_PASSWORD sysCRED_TYPE = 0x2 + sysCRED_TYPE_DOMAIN_CERTIFICATE sysCRED_TYPE = 0x3 + sysCRED_TYPE_DOMAIN_VISIBLE_PASSWORD sysCRED_TYPE = 0x4 + sysCRED_TYPE_GENERIC_CERTIFICATE sysCRED_TYPE = 0x5 + sysCRED_TYPE_DOMAIN_EXTENDED sysCRED_TYPE = 0x6 + + // https://docs.microsoft.com/en-us/windows/desktop/Debug/system-error-codes + sysERROR_NOT_FOUND = syscall.Errno(1168) + sysERROR_INVALID_PARAMETER = syscall.Errno(87) +) + +// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credreadw +func sysCredRead(targetName string, typ sysCRED_TYPE) (*Credential, error) { + var pcred *sysCREDENTIAL + targetNamePtr, _ := syscall.UTF16PtrFromString(targetName) + ret, _, err := procCredRead.Call( + uintptr(unsafe.Pointer(targetNamePtr)), + uintptr(typ), + 0, + uintptr(unsafe.Pointer(&pcred)), + ) + if ret == 0 { + return nil, err + } + defer procCredFree.Call(uintptr(unsafe.Pointer(pcred))) + + return sysToCredential(pcred), nil +} + +// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credwritew +func sysCredWrite(cred *Credential, typ sysCRED_TYPE) error { + ncred := sysFromCredential(cred) + ncred.Type = uint32(typ) + ret, _, err := procCredWrite.Call( + uintptr(unsafe.Pointer(ncred)), + 0, + ) + if ret == 0 { + return err + } + + return nil +} + +// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-creddeletew +func sysCredDelete(cred *Credential, typ sysCRED_TYPE) error { + targetNamePtr, _ := syscall.UTF16PtrFromString(cred.TargetName) + ret, _, err := procCredDelete.Call( + uintptr(unsafe.Pointer(targetNamePtr)), + uintptr(typ), + 0, + ) + if ret == 0 { + return err + } + + return nil +} + +// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credenumeratew +func sysCredEnumerate(filter string, all bool) ([]*Credential, error) { + var count int + var pcreds uintptr + var filterPtr *uint16 + if !all { + filterPtr, _ = syscall.UTF16PtrFromString(filter) + } + ret, _, err := procCredEnumerate.Call( + uintptr(unsafe.Pointer(filterPtr)), + 0, + uintptr(unsafe.Pointer(&count)), + uintptr(unsafe.Pointer(&pcreds)), + ) + if ret == 0 { + return nil, err + } + defer procCredFree.Call(pcreds) + credsSlice := *(*[]*sysCREDENTIAL)(unsafe.Pointer(&reflect.SliceHeader{ + Data: pcreds, + Len: count, + Cap: count, + })) + creds := make([]*Credential, count, count) + for i, cred := range credsSlice { + creds[i] = sysToCredential(cred) + } + + return creds, nil +} diff --git a/vendor/github.com/danieljoos/wincred/sys_unsupported.go b/vendor/github.com/danieljoos/wincred/sys_unsupported.go new file mode 100644 index 000000000..b47bccf8a --- /dev/null +++ b/vendor/github.com/danieljoos/wincred/sys_unsupported.go @@ -0,0 +1,36 @@ +// +build !windows + +package wincred + +import ( + "errors" + "syscall" +) + +const ( + sysCRED_TYPE_GENERIC = 0 + sysCRED_TYPE_DOMAIN_PASSWORD = 0 + sysCRED_TYPE_DOMAIN_CERTIFICATE = 0 + sysCRED_TYPE_DOMAIN_VISIBLE_PASSWORD = 0 + sysCRED_TYPE_GENERIC_CERTIFICATE = 0 + sysCRED_TYPE_DOMAIN_EXTENDED = 0 + + sysERROR_NOT_FOUND = syscall.Errno(1) + sysERROR_INVALID_PARAMETER = syscall.Errno(1) +) + +func sysCredRead(...interface{}) (*Credential, error) { + return nil, errors.New("Operation not supported") +} + +func sysCredWrite(...interface{}) error { + return errors.New("Operation not supported") +} + +func sysCredDelete(...interface{}) error { + return errors.New("Operation not supported") +} + +func sysCredEnumerate(...interface{}) ([]*Credential, error) { + return nil, errors.New("Operation not supported") +} diff --git a/vendor/github.com/danieljoos/wincred/types.go b/vendor/github.com/danieljoos/wincred/types.go new file mode 100644 index 000000000..28debc932 --- /dev/null +++ b/vendor/github.com/danieljoos/wincred/types.go @@ -0,0 +1,69 @@ +package wincred + +import ( + "time" +) + +// CredentialPersistence describes one of three persistence modes of a credential. +// A detailed description of the available modes can be found on +// Docs: https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw +type CredentialPersistence uint32 + +const ( + // PersistSession indicates that the credential only persists for the life + // of the current Windows login session. Such a credential is not visible in + // any other logon session, even from the same user. + PersistSession CredentialPersistence = 0x1 + + // PersistLocalMachine indicates that the credential persists for this and + // all subsequent logon sessions on this local machine/computer. It is + // however not visible for logon sessions of this user on a different + // machine. + PersistLocalMachine CredentialPersistence = 0x2 + + // PersistEnterprise indicates that the credential persists for this and all + // subsequent logon sessions for this user. It is also visible for logon + // sessions on different computers. + PersistEnterprise CredentialPersistence = 0x3 +) + +// CredentialAttribute represents an application-specific attribute of a credential. +type CredentialAttribute struct { + Keyword string + Value []byte +} + +// Credential is the basic credential structure. +// A credential is identified by its target name. +// The actual credential secret is available in the CredentialBlob field. +type Credential struct { + TargetName string + Comment string + LastWritten time.Time + CredentialBlob []byte + Attributes []CredentialAttribute + TargetAlias string + UserName string + Persist CredentialPersistence +} + +// GenericCredential holds a credential for generic usage. +// It is typically defined and used by applications that need to manage user +// secrets. +// +// More information about the available kinds of credentials of the Windows +// Credential Management API can be found on Docs: +// https://docs.microsoft.com/en-us/windows/desktop/SecAuthN/kinds-of-credentials +type GenericCredential struct { + Credential +} + +// DomainPassword holds a domain credential that is typically used by the +// operating system for user logon. +// +// More information about the available kinds of credentials of the Windows +// Credential Management API can be found on Docs: +// https://docs.microsoft.com/en-us/windows/desktop/SecAuthN/kinds-of-credentials +type DomainPassword struct { + Credential +} diff --git a/vendor/github.com/danieljoos/wincred/wincred.go b/vendor/github.com/danieljoos/wincred/wincred.go new file mode 100644 index 000000000..c5dbc2c97 --- /dev/null +++ b/vendor/github.com/danieljoos/wincred/wincred.go @@ -0,0 +1,111 @@ +// Package wincred provides primitives for accessing the Windows Credentials Management API. +// This includes functions for retrieval, listing and storage of credentials as well as Go structures for convenient access to the credential data. +// +// A more detailed description of Windows Credentials Management can be found on +// Docs: https://docs.microsoft.com/en-us/windows/desktop/SecAuthN/credentials-management +package wincred + +import "errors" + +const ( + // ErrElementNotFound is the error that is returned if a requested element cannot be found. + // This error constant can be used to check if a credential could not be found. + ErrElementNotFound = sysERROR_NOT_FOUND + + // ErrInvalidParameter is the error that is returned for invalid parameters. + // This error constant can be used to check if the given function parameters were invalid. + // For example when trying to create a new generic credential with an empty target name. + ErrInvalidParameter = sysERROR_INVALID_PARAMETER +) + +// GetGenericCredential fetches the generic credential with the given name from Windows credential manager. +// It returns nil and an error if the credential could not be found or an error occurred. +func GetGenericCredential(targetName string) (*GenericCredential, error) { + cred, err := sysCredRead(targetName, sysCRED_TYPE_GENERIC) + if cred != nil { + return &GenericCredential{*cred}, err + } + return nil, err +} + +// NewGenericCredential creates a new generic credential object with the given name. +// The persist mode of the newly created object is set to a default value that indicates local-machine-wide storage. +// The credential object is NOT yet persisted to the Windows credential vault. +func NewGenericCredential(targetName string) (result *GenericCredential) { + result = new(GenericCredential) + result.TargetName = targetName + result.Persist = PersistLocalMachine + return +} + +// Write persists the generic credential object to Windows credential manager. +func (t *GenericCredential) Write() (err error) { + err = sysCredWrite(&t.Credential, sysCRED_TYPE_GENERIC) + return +} + +// Delete removes the credential object from Windows credential manager. +func (t *GenericCredential) Delete() (err error) { + err = sysCredDelete(&t.Credential, sysCRED_TYPE_GENERIC) + return +} + +// GetDomainPassword fetches the domain-password credential with the given target host name from Windows credential manager. +// It returns nil and an error if the credential could not be found or an error occurred. +func GetDomainPassword(targetName string) (*DomainPassword, error) { + cred, err := sysCredRead(targetName, sysCRED_TYPE_DOMAIN_PASSWORD) + if cred != nil { + return &DomainPassword{*cred}, err + } + return nil, err +} + +// NewDomainPassword creates a new domain-password credential used for login to the given target host name. +// The persist mode of the newly created object is set to a default value that indicates local-machine-wide storage. +// The credential object is NOT yet persisted to the Windows credential vault. +func NewDomainPassword(targetName string) (result *DomainPassword) { + result = new(DomainPassword) + result.TargetName = targetName + result.Persist = PersistLocalMachine + return +} + +// Write persists the domain-password credential to Windows credential manager. +func (t *DomainPassword) Write() (err error) { + err = sysCredWrite(&t.Credential, sysCRED_TYPE_DOMAIN_PASSWORD) + return +} + +// Delete removes the domain-password credential from Windows credential manager. +func (t *DomainPassword) Delete() (err error) { + err = sysCredDelete(&t.Credential, sysCRED_TYPE_DOMAIN_PASSWORD) + return +} + +// SetPassword sets the CredentialBlob field of a domain password credential to the given string. +func (t *DomainPassword) SetPassword(pw string) { + t.CredentialBlob = utf16ToByte(utf16FromString(pw)) +} + +// List retrieves all credentials of the Credentials store. +func List() ([]*Credential, error) { + creds, err := sysCredEnumerate("", true) + if err != nil && errors.Is(err, ErrElementNotFound) { + // Ignore ERROR_NOT_FOUND and return an empty list instead + creds = []*Credential{} + err = nil + } + return creds, err +} + +// FilteredList retrieves the list of credentials from the Credentials store that match the given filter. +// The filter string defines the prefix followed by an asterisk for the `TargetName` attribute of the credentials. +func FilteredList(filter string) ([]*Credential, error) { + creds, err := sysCredEnumerate(filter, false) + if err != nil && errors.Is(err, ErrElementNotFound) { + // Ignore ERROR_NOT_FOUND and return an empty list instead + creds = []*Credential{} + err = nil + } + return creds, err +} diff --git a/vendor/github.com/godbus/dbus/v5/.travis.yml b/vendor/github.com/godbus/dbus/v5/.travis.yml new file mode 100644 index 000000000..dd6767204 --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/.travis.yml @@ -0,0 +1,50 @@ +dist: bionic +language: go +go_import_path: github.com/godbus/dbus + +go: + - 1.11.x + - 1.12.x + - 1.13.x + - tip + +matrix: + fast_finish: true + allow_failures: + - go: tip + +addons: + apt: + packages: + - dbus + - dbus-x11 + +before_install: + - export GO111MODULE=on + +script: + - go test -v -race -mod=readonly ./... # Run all the tests with the race detector enabled + - go vet ./... # go vet is the official Go static analyzer + +jobs: + include: + # The build matrix doesn't cover build stages, so manually expand + # the jobs with anchors + - &multiarch + stage: "Multiarch Test" + go: 1.11.x + env: TARGETS="386 arm arm64 ppc64le" + before_install: + - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + script: + - | + set -e + for target in $TARGETS; do + printf "\e[1mRunning test suite under ${target}.\e[0m\n" + GOARCH="$target" go test -v ./... + printf "\n\n" + done + - <<: *multiarch + go: 1.12.x + - <<: *multiarch + go: 1.13.x diff --git a/vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md b/vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md new file mode 100644 index 000000000..c88f9b2bd --- /dev/null +++ b/vendor/github.com/godbus/dbus/v5/CONTRIBUTING.md @@ -0,0 +1,50 @@ +# How to Contribute + +## Getting Started + +- Fork the repository on GitHub +- Read the [README](README.markdown) for build and test instructions +- Play with the project, submit bugs, submit patches! + +## Contribution Flow + +This is a rough outline of what a contributor's workflow looks like: + +- Create a topic branch from where you want to base your work (usually master). +- Make commits of logical units. +- Make sure your commit messages are in the proper format (see below). +- Push your changes to a topic branch in your fork of the repository. +- Make sure the tests pass, and add any new tests as appropriate. +- Submit a pull request to the original repository. + +Thanks for your contributions! + +### Format of the Commit Message + +We follow a rough convention for commit messages that is designed to answer two +questions: what changed and why. The subject line should feature the what and +the body of the commit should describe the why. + +``` +scripts: add the test-cluster command + +this uses tmux to setup a test cluster that you can easily kill and +start for debugging. + +Fixes #38 +``` + +The format can be described more formally as follows: + +``` +: + + + +