diff options
author | Leandro Alves <leandroalves@yahooinc.com> | 2021-11-16 14:45:05 +0100 |
---|---|---|
committer | Leandro Alves <leandroalves@yahooinc.com> | 2021-11-17 13:30:02 +0100 |
commit | 6e4ad0a351a6e7113ce72a3dffaa31f2aece8b22 (patch) | |
tree | 977a939cb68b32dc4d9847ca5c5101e1dbae3044 | |
parent | 1a1f46e024fccc5c783c93074e79e33279c97428 (diff) |
remove unused code and rename vars and methods
-rw-r--r-- | client/go/auth/auth.go | 36 | ||||
-rw-r--r-- | client/go/auth/token.go | 10 | ||||
-rw-r--r-- | client/go/cli/cli.go | 186 | ||||
-rw-r--r-- | client/go/cmd/helpers.go | 10 | ||||
-rw-r--r-- | client/go/cmd/login.go | 2 | ||||
-rw-r--r-- | client/go/vespa/target.go | 14 | ||||
-rw-r--r-- | client/go/vespa/target_test.go | 13 |
7 files changed, 108 insertions, 163 deletions
diff --git a/client/go/auth/auth.go b/client/go/auth/auth.go index 397e410924d..100af7ea1d2 100644 --- a/client/go/auth/auth.go +++ b/client/go/auth/auth.go @@ -4,7 +4,6 @@ package auth import ( "context" - "encoding/base64" "encoding/json" "errors" "fmt" @@ -15,7 +14,6 @@ import ( ) const ( - audiencePath = "/api/v2/" waitThresholdInSeconds = 3 // SecretsNamespace namespace used to set/get values from the keychain SecretsNamespace = "vespa-cli" @@ -39,8 +37,6 @@ type SecretStore interface { } type Result struct { - Tenant string - Domain string RefreshToken string AccessToken string ExpiresIn int64 @@ -114,17 +110,10 @@ func (a *Authenticator) Wait(ctx context.Context, state State) (Result, error) { return Result{}, errors.New(res.ErrorDescription) } - ten, domain, err := parseTenant(res.AccessToken) - if err != nil { - return Result{}, fmt.Errorf("cannot parse tenant from the given access token: %w", err) - } - return Result{ RefreshToken: res.RefreshToken, AccessToken: res.AccessToken, ExpiresIn: res.ExpiresIn, - Tenant: ten, - Domain: domain, }, nil } } @@ -148,28 +137,3 @@ func (a *Authenticator) getDeviceCode(ctx context.Context) (State, error) { } return res, nil } - -func parseTenant(accessToken string) (tenant, domain string, err error) { - parts := strings.Split(accessToken, ".") - v, err := base64.RawURLEncoding.DecodeString(parts[1]) - if err != nil { - return "", "", err - } - var payload struct { - AUDs []string `json:"aud"` - } - if err := json.Unmarshal(v, &payload); err != nil { - return "", "", err - } - for _, aud := range payload.AUDs { - u, err := url.Parse(aud) - if err != nil { - return "", "", err - } - if u.Path == audiencePath { - parts := strings.Split(u.Host, ".") - return parts[0], u.Host, nil - } - } - return "", "", fmt.Errorf("audience not found for %s", audiencePath) -} diff --git a/client/go/auth/token.go b/client/go/auth/token.go index e9b90b8994e..120c2602bad 100644 --- a/client/go/auth/token.go +++ b/client/go/auth/token.go @@ -25,16 +25,16 @@ type TokenRetriever struct { Client *http.Client } -// Delete deletes the given tenant from the secrets' storage. -func (t *TokenRetriever) Delete(tenant string) error { - return t.Secrets.Delete(SecretsNamespace, tenant) +// Delete deletes the given system from the secrets' storage. +func (t *TokenRetriever) Delete(system string) error { + return t.Secrets.Delete(SecretsNamespace, system) } // 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) { +func (t *TokenRetriever) Refresh(ctx context.Context, system string) (TokenResponse, error) { // get stored refresh token: - refreshToken, err := t.Secrets.Get(SecretsNamespace, tenant) + refreshToken, err := t.Secrets.Get(SecretsNamespace, system) if err != nil { return TokenResponse{}, fmt.Errorf("cannot get the stored refresh token: %w", err) } diff --git a/client/go/cli/cli.go b/client/go/cli/cli.go index e1dde387b89..22c40f195b4 100644 --- a/client/go/cli/cli.go +++ b/client/go/cli/cli.go @@ -7,9 +7,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/joeshaw/envdecode" - "github.com/pkg/browser" - "github.com/vespa-engine/vespa/client/go/util" "io/ioutil" "net/http" "os" @@ -19,8 +16,11 @@ import ( "sync" "time" + "github.com/joeshaw/envdecode" "github.com/lestrrat-go/jwx/jwt" + "github.com/pkg/browser" "github.com/vespa-engine/vespa/client/go/auth" + "github.com/vespa-engine/vespa/client/go/util" ) const accessTokenExpThreshold = 5 * time.Minute @@ -28,55 +28,24 @@ const accessTokenExpThreshold = 5 * time.Minute var errUnauthenticated = errors.New("not logged in. Try 'vespa login'") type config struct { - InstallID string `json:"install_id,omitempty"` - DefaultTenant string `json:"default_tenant"` - Tenants map[string]Tenant `json:"tenants"` + Systems map[string]System `json:"systems"` } -// Tenant is an auth0 Tenant. -type Tenant struct { - Name string `json:"name"` - Domain string `json:"domain"` - AccessToken string `json:"access_token,omitempty"` - Scopes []string `json:"scopes,omitempty"` - ExpiresAt time.Time `json:"expires_at"` - ClientID string `json:"client_id"` - ClientSecret string `json:"client_secret"` +type System struct { + AccessToken string `json:"access_token,omitempty"` + Scopes []string `json:"scopes,omitempty"` + ExpiresAt time.Time `json:"expires_at"` } type Cli struct { Authenticator *auth.Authenticator - tenant string + system string initOnce sync.Once errOnce error Path string config config } -// IsLoggedIn encodes the domain logic for determining whether we're -// logged in. This might check our config storage, or just in memory. -func (c *Cli) IsLoggedIn() bool { - // No need to check errors for initializing context. - _ = c.init() - - if c.tenant == "" { - return false - } - - // Parse the access token for the tenant. - t, err := jwt.ParseString(c.config.Tenants[c.tenant].AccessToken) - if err != nil { - return false - } - - // Check if token is valid. - if err = jwt.Validate(t, jwt.WithIssuer("https://vespa-cd.auth0.com/")); err != nil { - return false - } - - return true -} - // default to vespa-cd.auth0.com var ( authCfg struct { @@ -99,11 +68,12 @@ func ContextWithCancel() context.Context { return ctx } -// Setup will try to initialize the config context, as well as figure out if -// there's a readily available tenant. -func GetCli(configPath string) (*Cli, error) { +// GetCli will try to initialize the config context, as well as figure out if +// there's a readily available system. +func GetCli(configPath string, systemName string) (*Cli, error) { c := Cli{} c.Path = configPath + c.system = systemName if err := envdecode.StrictDecode(&authCfg); err != nil { return nil, fmt.Errorf("could not decode env: %w", err) } @@ -116,29 +86,49 @@ func GetCli(configPath string) (*Cli, error) { return &c, nil } -// prepareTenant loads the Tenant, refreshing its token if necessary. -// The Tenant access token needs a refresh if: -// 1. the Tenant scopes are different from the currently required scopes. -// 2. the access token is expired. -func (c *Cli) PrepareTenant(ctx context.Context) (Tenant, error) { - if err := c.init(); err != nil { - return Tenant{}, err +// IsLoggedIn encodes the domain logic for determining whether we're +// logged in. This might check our config storage, or just in memory. +func (c *Cli) IsLoggedIn() bool { + // No need to check errors for initializing context. + _ = c.init() + + if c.system == "" { + return false } - t, err := c.getTenant() + + // Parse the access token for the system. + token, err := jwt.ParseString(c.config.Systems[c.system].AccessToken) if err != nil { - return Tenant{}, err + return false } - if t.ClientID != "" && t.ClientSecret != "" { - return t, nil + // Check if token is valid. + if err = jwt.Validate(token, jwt.WithIssuer("https://vespa-cd.auth0.com/")); err != nil { + return false } - if t.AccessToken == "" || scopesChanged(t) { - t, err = RunLogin(ctx, c, true) + return true +} + +// PrepareSystem loads the System, refreshing its token if necessary. +// The System access token needs a refresh if: +// 1. the System scopes are different from the currently required scopes - (auth0 changes). +// 2. the access token is expired. +func (c *Cli) PrepareSystem(ctx context.Context) (System, error) { + if err := c.init(); err != nil { + return System{}, err + } + s, err := c.getSystem() + if err != nil { + return System{}, err + } + + if s.AccessToken == "" || scopesChanged(s) { + s, err = RunLogin(ctx, c, true) if err != nil { - return Tenant{}, err + return System{}, err } - } else if isExpired(t.ExpiresAt, accessTokenExpThreshold) { + } else if isExpired(s.ExpiresAt, accessTokenExpThreshold) { // check if the stored access token is expired: // use the refresh token to get a new access token: tr := &auth.TokenRetriever{ @@ -147,29 +137,29 @@ func (c *Cli) PrepareTenant(ctx context.Context) (Tenant, error) { Client: http.DefaultClient, } - res, err := tr.Refresh(ctx, t.Domain) + res, err := tr.Refresh(ctx, c.system) if err != nil { // ask and guide the user through the login process: fmt.Println(fmt.Errorf("failed to renew access token, %s", err)) - t, err = RunLogin(ctx, c, true) + s, err = RunLogin(ctx, c, true) if err != nil { - return Tenant{}, err + return System{}, err } } else { - // persist the updated tenant with renewed access token - t.AccessToken = res.AccessToken - t.ExpiresAt = time.Now().Add( + // persist the updated system with renewed access token + s.AccessToken = res.AccessToken + s.ExpiresAt = time.Now().Add( time.Duration(res.ExpiresIn) * time.Second, ) - err = c.AddTenant(t) + err = c.AddSystem(s) if err != nil { - return Tenant{}, err + return System{}, err } } } - return t, nil + return s, nil } // isExpired is true if now() + a threshold is after the given date @@ -177,11 +167,11 @@ func isExpired(t time.Time, threshold time.Duration) bool { return time.Now().Add(threshold).After(t) } -// scopesChanged compare the Tenant scopes +// scopesChanged compare the System scopes // with the currently required scopes. -func scopesChanged(t Tenant) bool { +func scopesChanged(s System) bool { want := auth.RequiredScopes() - got := t.Scopes + got := s.Scopes sort.Strings(want) sort.Strings(got) @@ -194,7 +184,7 @@ func scopesChanged(t Tenant) bool { return true } - for i := range t.Scopes { + for i := range s.Scopes { if want[i] != got[i] { return true } @@ -203,34 +193,30 @@ func scopesChanged(t Tenant) bool { return false } -func (c *Cli) getTenant() (Tenant, error) { +func (c *Cli) getSystem() (System, error) { if err := c.init(); err != nil { - return Tenant{}, err + return System{}, err } - t, ok := c.config.Tenants[c.tenant] + s, ok := c.config.Systems[c.system] if !ok { - return Tenant{}, fmt.Errorf("unable to find tenant: %s; run 'vespa login' to configure a new tenant", c.tenant) + return System{}, fmt.Errorf("unable to find system: %s; run 'vespa login' to configure a new system", c.system) } - return t, nil + return s, nil } -// AddTenant assigns an existing, or new Tenant. This is expected to be called +// AddSystem assigns an existing, or new System. This is expected to be called // after a login has completed. -func (c *Cli) AddTenant(ten Tenant) error { +func (c *Cli) AddSystem(s System) error { _ = c.init() - if c.config.DefaultTenant == "" { - c.config.DefaultTenant = ten.Domain - } - // If we're dealing with an empty file, we'll need to initialize this map. - if c.config.Tenants == nil { - c.config.Tenants = map[string]Tenant{} + if c.config.Systems == nil { + c.config.Systems = map[string]System{} } - c.config.Tenants[ten.Domain] = ten + c.config.Systems[c.system] = s if err := c.persistConfig(); err != nil { return fmt.Errorf("unexpected error persisting config: %w", err) @@ -282,14 +268,6 @@ func (c *Cli) initContext() (err error) { return err } - if c.tenant == "" && c.config.DefaultTenant == "" { - return errUnauthenticated - } - - if c.tenant == "" { - c.tenant = c.config.DefaultTenant - } - return nil } @@ -297,14 +275,14 @@ func (c *Cli) initContext() (err error) { // by showing the login instructions, opening the browser. // Use `expired` to run the login from other commands setup: // this will only affect the messages. -func RunLogin(ctx context.Context, cli *Cli, expired bool) (Tenant, error) { +func RunLogin(ctx context.Context, c *Cli, expired bool) (System, error) { if expired { fmt.Println("Please sign in to re-authorize the CLI.") } - state, err := cli.Authenticator.Start(ctx) + state, err := c.Authenticator.Start(ctx) if err != nil { - return Tenant{}, fmt.Errorf("could not start the authentication process: %w", err) + return System{}, fmt.Errorf("could not start the authentication process: %w", err) } fmt.Printf("Your Device Confirmation code is: %s\n\n", state.UserCode) @@ -319,12 +297,12 @@ func RunLogin(ctx context.Context, cli *Cli, expired bool) (Tenant, error) { var res auth.Result err = util.Spinner("Waiting for login to complete in browser", func() error { - res, err = cli.Authenticator.Wait(ctx, state) + res, err = c.Authenticator.Wait(ctx, state) return err }) if err != nil { - return Tenant{}, fmt.Errorf("login error: %w", err) + return System{}, fmt.Errorf("login error: %w", err) } fmt.Print("\n") @@ -333,23 +311,21 @@ func RunLogin(ctx context.Context, cli *Cli, expired bool) (Tenant, error) { // store the refresh token secretsStore := &auth.Keyring{} - err = secretsStore.Set(auth.SecretsNamespace, res.Domain, res.RefreshToken) + err = secretsStore.Set(auth.SecretsNamespace, c.system, res.RefreshToken) if err != nil { // log the error but move on fmt.Println("Could not store the refresh token locally, please expect to login again once your access token expired.") } - t := Tenant{ - Name: res.Tenant, - Domain: res.Domain, + s := System{ AccessToken: res.AccessToken, ExpiresAt: time.Now().Add(time.Duration(res.ExpiresIn) * time.Second), Scopes: auth.RequiredScopes(), } - err = cli.AddTenant(t) + err = c.AddSystem(s) if err != nil { - return Tenant{}, fmt.Errorf("could not add tenant to config: %w", err) + return System{}, fmt.Errorf("could not add system to config: %w", err) } - return t, nil + return s, nil } diff --git a/client/go/cmd/helpers.go b/client/go/cmd/helpers.go index 702f50ce9f5..084148f9bdc 100644 --- a/client/go/cmd/helpers.go +++ b/client/go/cmd/helpers.go @@ -144,6 +144,13 @@ func getService(service string, sessionOrRunID int64) *vespa.Service { func getSystem() string { return os.Getenv("VESPA_CLI_CLOUD_SYSTEM") } +func getSystemName() string { + if getSystem() == "publiccd" { + return "publiccd" + } + return "public" +} + func getConsoleURL() string { if getSystem() == "publiccd" { return "https://console-cd.vespa.oath.cloud" @@ -205,7 +212,8 @@ func getTarget() vespa.Target { Writer: stdout, Level: vespa.LogLevel(logLevelArg), }, - cfg.AuthConfigPath()) + cfg.AuthConfigPath(), + getSystemName()) } fatalErrHint(fmt.Errorf("Invalid target: %s", targetType), "Valid targets are 'local', 'cloud' or an URL") return nil diff --git a/client/go/cmd/login.go b/client/go/cmd/login.go index 767d462b0be..415d44b75db 100644 --- a/client/go/cmd/login.go +++ b/client/go/cmd/login.go @@ -24,7 +24,7 @@ var loginCmd = &cobra.Command{ if err != nil { return err } - c, err := cli.GetCli(cfg.AuthConfigPath()) + c, err := cli.GetCli(cfg.AuthConfigPath(), getSystemName()) if err != nil { return err } diff --git a/client/go/vespa/target.go b/client/go/vespa/target.go index f497bf5b3cd..367685df34d 100644 --- a/client/go/vespa/target.go +++ b/client/go/vespa/target.go @@ -1,4 +1,5 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + package vespa import ( @@ -6,7 +7,6 @@ import ( "crypto/tls" "encoding/json" "fmt" - "github.com/vespa-engine/vespa/client/go/cli" "io" "io/ioutil" "math" @@ -18,6 +18,7 @@ import ( "strings" "time" + "github.com/vespa-engine/vespa/client/go/cli" "github.com/vespa-engine/vespa/client/go/util" ) @@ -211,6 +212,7 @@ type cloudTarget struct { queryURL string documentURL string authConfigPath string + systemName string } func (t *cloudTarget) Type() string { return t.targetType } @@ -253,12 +255,12 @@ func (t *cloudTarget) PrepareApiRequest(req *http.Request, sigKeyId string) erro } func (t *cloudTarget) addAuth0AccessToken(request *http.Request) error { - c, err := cli.GetCli(t.authConfigPath) - tenant, err := c.PrepareTenant(cli.ContextWithCancel()) + c, err := cli.GetCli(t.authConfigPath, t.systemName) + system, err := c.PrepareSystem(cli.ContextWithCancel()) if err != nil { return err } - request.Header.Set("Authorization", "Bearer "+tenant.AccessToken) + request.Header.Set("Authorization", "Bearer "+system.AccessToken) return nil } @@ -446,8 +448,7 @@ func CustomTarget(baseURL string) Target { } // CloudTarget creates a Target for the Vespa Cloud platform. -func CloudTarget(apiURL string, deployment Deployment, apiKey []byte, tlsOptions TLSOptions, logOptions LogOptions, - authConfigPath string) Target { +func CloudTarget(apiURL string, deployment Deployment, apiKey []byte, tlsOptions TLSOptions, logOptions LogOptions, authConfigPath string, systemName string) Target { return &cloudTarget{ apiURL: apiURL, targetType: cloudTargetType, @@ -456,6 +457,7 @@ func CloudTarget(apiURL string, deployment Deployment, apiKey []byte, tlsOptions tlsOptions: tlsOptions, logOptions: logOptions, authConfigPath: authConfigPath, + systemName: systemName, } } diff --git a/client/go/vespa/target_test.go b/client/go/vespa/target_test.go index d4d23901513..62bde3044bf 100644 --- a/client/go/vespa/target_test.go +++ b/client/go/vespa/target_test.go @@ -149,15 +149,10 @@ func createCloudTarget(t *testing.T, url string, logWriter io.Writer) Target { } assert.Nil(t, err) - target := CloudTarget( - "https://example.com", - Deployment{ - Application: ApplicationID{Tenant: "t1", Application: "a1", Instance: "i1"}, - Zone: ZoneID{Environment: "dev", Region: "us-north-1"}, - }, - apiKey, - TLSOptions{KeyPair: x509KeyPair}, - LogOptions{Writer: logWriter}, "") + target := CloudTarget("https://example.com", Deployment{ + Application: ApplicationID{Tenant: "t1", Application: "a1", Instance: "i1"}, + Zone: ZoneID{Environment: "dev", Region: "us-north-1"}, + }, apiKey, TLSOptions{KeyPair: x509KeyPair}, LogOptions{Writer: logWriter}, "", "") if ct, ok := target.(*cloudTarget); ok { ct.apiURL = url } else { |