Add skip daemon flag and send recently authed tokens to daemon (#3883)

* Add skip daemon flag and send recently authed tokens to daemon

* Rename flag skip-daemon to skip-cache-daemon

* adding option test

* move common.Options to base.Option

* SocketAddress returns *url.URL, fix search test
This commit is contained in:
Todd authored and Johan Brandhorst-Satzkorn committed 2024-01-05 07:43:20 -08:00
1 parent 2f7459fa40
commit 3a7ea64a31
27 files changed
+429 -419

No files matched your search

+20 -16
View File
@@ -64,8 +64,19 @@ func (c *AddTokenCommand) Run(args []string) int {
c.PrintCliError(err)
return base.CommandUserError
}
client, err := c.Client()
if err != nil {
c.PrintCliError(err)
return base.CommandCliError
}
apiErr, err := c.Add(ctx)
keyringType, tokenName, err := c.DiscoverKeyringTokenInfo()
if err != nil {
c.PrintCliError(err)
return base.CommandCliError
}
apiErr, err := c.Add(ctx, client, keyringType, tokenName)
if err != nil {
c.PrintCliError(err)
return base.CommandCliError
@@ -78,24 +89,14 @@ func (c *AddTokenCommand) Run(args []string) int {
return base.CommandSuccess
}
func (c *AddTokenCommand) Add(ctx context.Context) (*api.Error, error) {
func (c *AddTokenCommand) Add(ctx context.Context, client *api.Client, keyringType, tokenName string) (*api.Error, error) {
const op = "daemon.(AddTokenCommand).Add"
client, err := c.Client()
if err != nil {
return nil, err
}
keyringType, tokenName, err := c.DiscoverKeyringTokenInfo()
if err != nil {
return nil, err
}
pa := daemon.UpsertTokenRequest{
BoundaryAddr: client.Addr(),
}
switch keyringType {
case "", base.NoneKeyring:
keyringType = base.NoneKeyring
token := client.Token()
if parts := strings.SplitN(token, "_", 4); len(parts) == 3 {
pa.AuthTokenId = strings.Join(parts[:2], "_")
@@ -129,12 +130,15 @@ func addToken(ctx context.Context, daemonPath string, p *daemon.UpsertTokenReque
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
addr := daemon.SocketAddress(daemonPath)
_, err = os.Stat(strings.TrimPrefix(addr, "unix://"))
if strings.HasPrefix(addr, "unix://") && err != nil {
addr, err := daemon.SocketAddress(daemonPath)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if err := client.SetAddr(addr); err != nil {
_, err = os.Stat(addr.Path)
if strings.EqualFold(addr.Scheme, "unix") && err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if err := client.SetAddr(addr.String()); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
// Because this is using the real lib it can pick up from stored locations
@@ -10,24 +10,31 @@ import (
"os"
"os/exec"
"strings"
"time"
"github.com/hashicorp/boundary/internal/clientcache/internal/daemon"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/errors"
"github.com/mitchellh/cli"
)
// Keep this interface aligned with the interface at internal/cmd/commands.go
type cacheEnabledCommand interface {
cli.Command
BaseCommand() *base.Command
}
// CommandWrapper starts the boundary daemon after the command was Run and attempts
// to send the current persona to any running daemon.
type CommandWrapper struct {
cli.Command
ui cli.Ui
cacheEnabledCommand
}
// Wrap returns a cli.CommandFactory that returns a command wrapped in the CommandWrapper.
func Wrap(ui cli.Ui, wrapped cli.Command) cli.CommandFactory {
func Wrap(c cacheEnabledCommand) cli.CommandFactory {
return func() (cli.Command, error) {
return &CommandWrapper{
Command: wrapped,
ui: ui,
cacheEnabledCommand: c,
}, nil
}
}
@@ -35,11 +42,18 @@ func Wrap(ui cli.Ui, wrapped cli.Command) cli.CommandFactory {
// Run runs the wrapped command and then attempts to start the boundary daemon and send
// the current persona
func (w *CommandWrapper) Run(args []string) int {
r := w.Command.Run(args)
if w.BaseCommand().FlagSkipCacheDaemon {
return w.cacheEnabledCommand.Run(args)
}
// potentially intercept the token in case it isn't stored in the keyring
var token string
w.cacheEnabledCommand.BaseCommand().Opts = append(w.cacheEnabledCommand.BaseCommand().Opts, base.WithInterceptedToken(&token))
r := w.cacheEnabledCommand.Run(args)
ctx := context.Background()
if w.startDaemon(ctx) {
w.addPersonaInCache(ctx)
w.addTokenToCache(ctx, token)
}
return r
}
@@ -49,7 +63,7 @@ func (w *CommandWrapper) Run(args []string) int {
func (w *CommandWrapper) startDaemon(ctx context.Context) bool {
cmdName, err := os.Executable()
if err != nil {
w.ui.Error(fmt.Sprintf("unable to find boundary binary for daemon startup: %s", err.Error()))
w.BaseCommand().UI.Error(fmt.Sprintf("unable to find boundary binary for daemon startup: %s", err.Error()))
return false
}
@@ -64,10 +78,59 @@ func (w *CommandWrapper) startDaemon(ctx context.Context) bool {
return err == nil || strings.Contains(stdErr.String(), "already running")
}
// addPersonaInCache runs AddPersonaCommand
func (w *CommandWrapper) addPersonaInCache(ctx context.Context) bool {
c := AddTokenCommand{Command: base.NewCommand(w.ui)}
c.Flags()
apiErr, err := c.Add(ctx)
// addTokenToCache runs AddTokenCommand with the token used in, or retrieved by
// the wrapped command.
func (w *CommandWrapper) addTokenToCache(ctx context.Context, token string) bool {
com := AddTokenCommand{Command: base.NewCommand(w.BaseCommand().UI)}
client, err := w.BaseCommand().Client()
if err != nil {
return false
}
keyringType, tokName, err := w.BaseCommand().DiscoverKeyringTokenInfo()
if err != nil {
return false
}
if token != "" {
client.SetToken(token)
}
// Since the daemon might have just started, we need to wait until it can
// respond to our requests
waitCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
if err := waitForDaemon(waitCtx); err != nil {
// TODO: Print the result of this out into a log in the dot directory
return false
}
apiErr, err := com.Add(ctx, client, keyringType, tokName)
return err == nil && apiErr == nil
}
// waitForDaemon continually looks for the unix socket until it is found or the
// provided context is done. It returns an error if the unix socket is not found
// before the context is done.
func waitForDaemon(ctx context.Context) error {
const op = "daemon.waitForDaemon"
dotPath, err := DefaultDotDirectory(ctx)
if err != nil {
return err
}
timer := time.NewTimer(0)
addr, err := daemon.SocketAddress(dotPath)
if err != nil {
return errors.Wrap(ctx, err, op)
}
_, err = os.Stat(addr.Path)
for os.IsNotExist(err) {
select {
case <-timer.C:
case <-ctx.Done():
return ctx.Err()
}
_, err = os.Stat(addr.Path)
timer.Reset(10 * time.Millisecond)
}
return nil
}
@@ -69,12 +69,15 @@ func (s *StopCommand) stop(ctx context.Context) error {
func stopThroughHandler(ctx context.Context, dotPath string) (*api.Error, error) {
const op = "daemon.stopThroughHandler"
sockAddr := daemon.SocketAddress(dotPath)
sockAddr, err := daemon.SocketAddress(dotPath)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
client, err := api.NewClient(nil)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if err := client.SetAddr(sockAddr); err != nil {
if err := client.SetAddr(sockAddr.String()); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
// Because this is using the real lib it can pick up from stored locations
+3 -3
View File
@@ -169,14 +169,14 @@ func search(ctx context.Context, daemonPath string, fb filterBy) (*api.Response,
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
addr := daemon.SocketAddress(daemonPath)
addr, err := daemon.SocketAddress(daemonPath)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
if _, err := os.Stat(strings.TrimPrefix(addr, "unix://")); strings.HasPrefix(addr, "unix://") && err == os.ErrNotExist {
if _, err := os.Stat(addr.Path); addr.Scheme == "unix" && err == os.ErrNotExist {
return nil, errors.New(ctx, errors.Internal, op, "daemon unix socket is not setup")
}
if err := client.SetAddr(addr); err != nil {
if err := client.SetAddr(addr.String()); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
+1 -12
View File
@@ -25,24 +25,12 @@ type testCommander struct {
at *authtokens.AuthToken
}
func (r *testCommander) keyring() string {
return r.at.Id
}
func (r *testCommander) tokenName() string {
return r.at.Id
}
func (r *testCommander) Client(opt ...base.Option) (*api.Client, error) {
client, err := api.NewClient(nil)
require.NoError(r.t, err)
return client, nil
}
func (r *testCommander) DiscoverKeyringTokenInfo() (string, string, error) {
return r.keyring(), r.tokenName(), nil
}
func (r *testCommander) ReadTokenFromKeyring(k, a string) *authtokens.AuthToken {
return r.at
}
@@ -72,6 +60,7 @@ func TestSearch(t *testing.T) {
}()
// Give the store some time to get initialized
time.Sleep(100 * time.Millisecond)
srv.AddKeyringToken(t, "address", "keyringtype", "tokenname", at.Id, boundaryTokenReaderFn)
errorCases := []struct {
name string
@@ -7,6 +7,7 @@ import (
"context"
"fmt"
"net"
"net/url"
"os"
"path/filepath"
@@ -52,8 +53,7 @@ func listener(ctx context.Context, path string) (net.Listener, error) {
return l, nil
}
// SocketAddress returns the unix socket filename with a 'unix://' prefix.
// The returned value can be passed directly into the api.Client
func SocketAddress(path string) string {
return fmt.Sprintf("unix://%s", filepath.Join(path, sockAddr))
// SocketAddress returns the unix socket *url.URL with the scheme set to 'unix'.
func SocketAddress(path string) (*url.URL, error) {
return url.Parse(fmt.Sprintf("unix://%s", filepath.Join(path, sockAddr)))
}
@@ -45,7 +45,9 @@ func TestListenComms(t *testing.T) {
client, err := api.NewClient(nil)
require.NoError(t, err)
require.NoError(t, client.SetAddr(SocketAddress(path)))
u, err := SocketAddress(path)
require.NoError(t, err)
require.NoError(t, client.SetAddr(u.String()))
client.SetToken("")
req, err := client.NewRequest(ctx, "GET", "/test", nil)
require.NoError(t, err)
@@ -35,7 +35,6 @@ import (
// and retrieve the keyring and token information used by a command.
type Commander interface {
ClientProvider
DiscoverKeyringTokenInfo() (string, string, error)
ReadTokenFromKeyring(keyringType, tokenName string) *authtokens.AuthToken
}
@@ -207,27 +206,6 @@ func (s *CacheServer) Serve(ctx context.Context, cmd Commander, opt ...Option) e
return errors.Wrap(ctx, err, op)
}
// If we have a token info already, add it to the repository immediately so
// it can start to get updated.
func() {
client, err := cmd.Client()
if err != nil {
event.WriteError(ctx, op, err)
return
}
krType, tokName, err := cmd.DiscoverKeyringTokenInfo()
if err != nil {
event.WriteError(ctx, op, err)
return
}
if at := cmd.ReadTokenFromKeyring(krType, tokName); at != nil {
if err := repo.AddKeyringToken(ctx, client.Addr(), cache.KeyringToken{KeyringType: krType, TokenName: tokName, AuthTokenId: at.Id}); err != nil {
event.WriteError(ctx, op, err)
return
}
}
}()
refreshService, err := cache.NewRefreshService(ctx, repo)
if err != nil {
return errors.Wrap(ctx, err, op)
@@ -68,6 +68,19 @@ func (s *TestServer) Serve(t *testing.T, opt ...Option) error {
return s.CacheServer.Serve(ctx, s.cmd, opt...)
}
func (s *TestServer) AddKeyringToken(t *testing.T, address, keyring, tokenName, tokenId string, atReadFn cache.BoundaryTokenReaderFn) {
t.Helper()
ctx := context.Background()
r, err := cache.NewRepository(ctx, s.CacheServer.store, &sync.Map{}, s.cmd.ReadTokenFromKeyring, atReadFn)
require.NoError(t, err)
require.NoError(t, r.AddKeyringToken(ctx, address, cache.KeyringToken{
KeyringType: keyring,
TokenName: tokenName,
AuthTokenId: tokenId,
}))
}
// AddResources adds targets to the cache for the provided address, token name,
// and keyring type. They token info must already be known to the server.
func (s *TestServer) AddResources(t *testing.T, p *authtokens.AuthToken, tars []*targets.Target, sess []*sessions.Session, atReadFn cache.BoundaryTokenReaderFn) {
+21 -5
View File
@@ -64,10 +64,11 @@ const (
// maxLineLength is the maximum width of any line.
maxLineLength int = 78
envToken = "BOUNDARY_TOKEN"
EnvTokenName = "BOUNDARY_TOKEN_NAME"
EnvKeyringType = "BOUNDARY_KEYRING_TYPE"
envRecoveryConfig = "BOUNDARY_RECOVERY_CONFIG"
envToken = "BOUNDARY_TOKEN"
EnvTokenName = "BOUNDARY_TOKEN_NAME"
EnvKeyringType = "BOUNDARY_KEYRING_TYPE"
envRecoveryConfig = "BOUNDARY_RECOVERY_CONFIG"
envSkipCacheDaemon = "BOUNDARY_SKIP_CACHE_DAEMON"
StoredTokenName = "HashiCorp Boundary Auth Token"
)
@@ -84,6 +85,8 @@ type Command struct {
UI cli.Ui
ShutdownCh chan struct{}
Opts []Option
flags *FlagSets
flagsOnce sync.Once
@@ -103,6 +106,7 @@ type Command struct {
FlagKeyringType string
FlagRecoveryConfig string
FlagOutputCurlString bool
FlagSkipCacheDaemon bool
FlagScopeId string
FlagScopeName string
@@ -189,6 +193,10 @@ func MakeShutdownCh() chan struct{} {
return resultCh
}
func (c *Command) BaseCommand() *Command {
return c
}
// Client returns the HTTP API client. The client is cached on the command to
// save performance on future calls.
func (c *Command) Client(opt ...Option) (*api.Client, error) {
@@ -197,7 +205,7 @@ func (c *Command) Client(opt ...Option) (*api.Client, error) {
return c.client, nil
}
opts := getOpts(opt...)
opts := GetOpts(opt...)
config, err := api.DefaultConfig()
if err != nil {
@@ -455,6 +463,14 @@ func (c *Command) FlagSet(bit FlagSetBit) *FlagSets {
Target: &c.FlagOutputCurlString,
Usage: "Instead of executing the request, print an equivalent cURL command string and exit.",
})
f.BoolVar(&BoolVar{
Name: "skip-cache-daemon",
Target: &c.FlagSkipCacheDaemon,
Default: false,
EnvVar: envSkipCacheDaemon,
Usage: "Skips starting the caching daemon or sending the current used/retrieved token to the caching daemon.",
})
}
if bit&FlagSetOutputFormat != 0 {
+1 -1
View File
@@ -35,7 +35,7 @@ func (b *Server) CreateDevDatabase(ctx context.Context, opt ...Option) error {
var err error
var c func() error
opts := getOpts(opt...)
opts := GetOpts(opt...)
// We should only get back postgres for now, but laying the foundation for non-postgres
switch opts.withDialect {
+2 -2
View File
@@ -182,7 +182,7 @@ func WrapMap(prefixSpaces, maxLengthOverride int, input map[string]any) string {
// information, to the UI in the appropriate format. WithAttributeFieldPrefix is
// used, all other options are ignored.
func (c *Command) PrintApiError(in *api.Error, contextStr string, opt ...Option) {
opts := getOpts(opt...)
opts := GetOpts(opt...)
switch Format(c.UI) {
case "json":
var b []byte
@@ -310,7 +310,7 @@ func (c *Command) PrintJsonItem(resp *api.Response, opt ...Option) bool {
// PrintJson prints the given raw JSON in our common format
func (c *Command) PrintJson(input json.RawMessage, opt ...Option) bool {
opts := getOpts(opt...)
opts := GetOpts(opt...)
output := struct {
StatusCode int `json:"status_code,omitempty"`
Item json.RawMessage `json:"item,omitempty"`
+20 -2
View File
@@ -9,8 +9,8 @@ import (
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
)
// getOpts - iterate the inbound Options and return a struct.
func getOpts(opt ...Option) Options {
// GetOpts - iterate the inbound Options and return a struct.
func GetOpts(opt ...Option) Options {
opts := getDefaultOptions()
for _, o := range opt {
if o != nil {
@@ -45,6 +45,8 @@ type Options struct {
withHostPlugin func() (string, plugin.HostPluginServiceClient)
withEventGating bool
withImplicitId string
WithSkipScopeIdFlag bool
WithInterceptedToken *string
}
func getDefaultOptions() Options {
@@ -209,3 +211,19 @@ func WithImplicitId(with string) Option {
o.withImplicitId = with
}
}
// WithSkipScopeIdFlag tells a command to not create a scope ID flag (usually
// because it's already been defined)
func WithSkipScopeIdFlag(with bool) Option {
return func(o *Options) {
o.WithSkipScopeIdFlag = with
}
}
// WithInterceptedToken provides a string pointer that will have the token
// assigned to it when performing an authenticate command.
func WithInterceptedToken(s *string) Option {
return func(o *Options) {
o.WithInterceptedToken = s
}
}
+32 -15
View File
@@ -15,7 +15,7 @@ func Test_GetOpts(t *testing.T) {
t.Parallel()
t.Run("nil-options", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(nil, nil)
opts := GetOpts(nil, nil)
testOpts := getDefaultOptions()
assert.Equal(opts, testOpts)
})
@@ -28,7 +28,7 @@ func Test_GetOpts(t *testing.T) {
ObservationsEnabled: &isTrue,
SysEventsEnabled: &isTrue,
}
opts := getOpts(WithEventFlags(&f))
opts := GetOpts(WithEventFlags(&f))
testOpts := getDefaultOptions()
testOpts.withEventFlags = &f
assert.Equal(opts, testOpts)
@@ -45,84 +45,84 @@ func Test_GetOpts(t *testing.T) {
},
},
}
opts := getOpts(WithEventerConfig(&c))
opts := GetOpts(WithEventerConfig(&c))
testOpts := getDefaultOptions()
testOpts.withEventerConfig = &c
assert.Equal(opts, testOpts)
})
t.Run("WithNoTokenScope", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithNoTokenScope())
opts := GetOpts(WithNoTokenScope())
testOpts := getDefaultOptions()
testOpts.withNoTokenScope = true
assert.Equal(opts, testOpts)
})
t.Run("WithSkipDatabaseDestruction", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithSkipDatabaseDestruction())
opts := GetOpts(WithSkipDatabaseDestruction())
testOpts := getDefaultOptions()
testOpts.withSkipDatabaseDestruction = true
assert.Equal(opts, testOpts)
})
t.Run("WithNoTokenValue", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithNoTokenValue())
opts := GetOpts(WithNoTokenValue())
testOpts := getDefaultOptions()
testOpts.withNoTokenValue = true
assert.Equal(opts, testOpts)
})
t.Run("WithSkipAuthMethodCreation", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithSkipAuthMethodCreation())
opts := GetOpts(WithSkipAuthMethodCreation())
testOpts := getDefaultOptions()
testOpts.withSkipAuthMethodCreation = true
assert.Equal(opts, testOpts)
})
t.Run("WithSkipOidcAuthMethodCreation", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithSkipOidcAuthMethodCreation())
opts := GetOpts(WithSkipOidcAuthMethodCreation())
testOpts := getDefaultOptions()
testOpts.withSkipOidcAuthMethodCreation = true
assert.Equal(opts, testOpts)
})
t.Run("WithSkipLdapAuthMethodCreation", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithSkipLdapAuthMethodCreation())
opts := GetOpts(WithSkipLdapAuthMethodCreation())
testOpts := getDefaultOptions()
testOpts.withSkipLdapAuthMethodCreation = true
assert.Equal(opts, testOpts)
})
t.Run("WithSkipScopesCreation", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithSkipScopesCreation())
opts := GetOpts(WithSkipScopesCreation())
testOpts := getDefaultOptions()
testOpts.withSkipScopesCreation = true
assert.Equal(opts, testOpts)
})
t.Run("WithSkipHostResourcesCreation", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithSkipHostResourcesCreation())
opts := GetOpts(WithSkipHostResourcesCreation())
testOpts := getDefaultOptions()
testOpts.withSkipHostResourcesCreation = true
assert.Equal(opts, testOpts)
})
t.Run("WithSkipTargetCreation", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithSkipTargetCreation())
opts := GetOpts(WithSkipTargetCreation())
testOpts := getDefaultOptions()
testOpts.withSkipTargetCreation = true
assert.Equal(opts, testOpts)
})
t.Run("WithContainerImage", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(WithContainerImage("test-container"))
opts := GetOpts(WithContainerImage("test-container"))
testOpts := getDefaultOptions()
testOpts.withContainerImage = "test-container"
assert.Equal(opts, testOpts)
})
t.Run("withDialect", func(t *testing.T) {
assert := assert.New(t)
opts := getOpts(withDialect("test-dialect"))
opts := GetOpts(withDialect("test-dialect"))
testOpts := getDefaultOptions()
testOpts.withDialect = "test-dialect"
assert.Equal(opts, testOpts)
@@ -131,7 +131,24 @@ func Test_GetOpts(t *testing.T) {
assert := assert.New(t)
testOpts := getDefaultOptions()
assert.False(testOpts.withEventGating)
opts := getOpts(WithEventGating(true))
opts := GetOpts(WithEventGating(true))
assert.True(opts.withEventGating)
})
t.Run("WithSkipScopeIdFlag", func(t *testing.T) {
assert := assert.New(t)
opts := GetOpts(WithSkipScopeIdFlag(true))
testOpts := getDefaultOptions()
testOpts.WithSkipScopeIdFlag = true
assert.Equal(opts, testOpts)
})
t.Run("WithSkipScopeIdFlag", func(t *testing.T) {
assert := assert.New(t)
var s string
opts := GetOpts(WithInterceptedToken(&s))
testOpts := getDefaultOptions()
testOpts.WithInterceptedToken = &s
assert.Equal(opts, testOpts)
})
}
+1 -1
View File
@@ -175,7 +175,7 @@ func (b *Server) SetupEventing(ctx context.Context, logger hclog.Logger, seriali
if serverName == "" {
return berrors.New(ctx, berrors.InvalidParameter, op, "missing server name")
}
opts := getOpts(opt...)
opts := GetOpts(opt...)
if opts.withEventerConfig != nil {
if err := opts.withEventerConfig.Validate(); err != nil {
return berrors.Wrap(ctx, err, op, berrors.WithMsg("invalid eventer config"))
+187 -194
View File
File diff suppressed because it is too large. Load diff
@@ -11,7 +11,6 @@ import (
"github.com/hashicorp/boundary/api/authmethods"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/common"
"github.com/hashicorp/boundary/internal/types/scope"
"github.com/mitchellh/cli"
"github.com/mitchellh/go-wordwrap"
@@ -104,15 +103,18 @@ func (c *Command) Run(args []string) int {
switch {
case strings.HasPrefix(c.FlagAuthMethodId, globals.PasswordAuthMethodPrefix):
cmd := PasswordCommand{Command: c.Command, Opts: []common.Option{common.WithSkipScopeIdFlag(true)}}
cmd := PasswordCommand{Command: c.Command}
cmd.Opts = append(c.Opts, base.WithSkipScopeIdFlag(true))
cmd.Run([]string{})
case strings.HasPrefix(c.FlagAuthMethodId, globals.OidcAuthMethodPrefix):
cmd := OidcCommand{Command: c.Command, Opts: []common.Option{common.WithSkipScopeIdFlag(true)}}
cmd := OidcCommand{Command: c.Command}
cmd.Opts = append(c.Opts, base.WithSkipScopeIdFlag(true))
cmd.Run([]string{})
case strings.HasPrefix(c.FlagAuthMethodId, globals.LdapAuthMethodPrefix):
cmd := LdapCommand{Command: c.Command, Opts: []common.Option{common.WithSkipScopeIdFlag(true)}}
cmd := LdapCommand{Command: c.Command}
cmd.Opts = append(c.Opts, base.WithSkipScopeIdFlag(true))
cmd.Run([]string{})
default:
+6 -1
View File
@@ -18,12 +18,13 @@ import (
zkeyring "github.com/zalando/go-keyring"
)
func saveAndOrPrintToken(c *base.Command, result *authmethods.AuthenticateResult) int {
func saveAndOrPrintToken(c *base.Command, result *authmethods.AuthenticateResult, opt ...base.Option) int {
token := new(authtokens.AuthToken)
if err := json.Unmarshal(result.GetRawAttributes(), token); err != nil {
c.PrintCliError(fmt.Errorf("Error trying to decode response as an auth token: %w", err))
return base.CommandCliError
}
opts := base.GetOpts(opt...)
switch base.Format(c.UI) {
case "table":
@@ -101,6 +102,10 @@ func saveAndOrPrintToken(c *base.Command, result *authmethods.AuthenticateResult
c.UI.Warn("\nStoring the token in a keyring was disabled. The token is:")
c.UI.Output(token.Token)
c.UI.Warn("Please be sure to store it safely!")
if opts.WithInterceptedToken != nil {
*opts.WithInterceptedToken = token.Token
}
}
return base.CommandSuccess
+4 -11
View File
@@ -13,7 +13,6 @@ import (
"github.com/hashicorp/boundary/api/authmethods"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/common"
"github.com/hashicorp/boundary/internal/types/scope"
"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/go-secure-stdlib/password"
@@ -32,8 +31,7 @@ type LdapCommand struct {
flagLoginName string
flagPassword string
Opts []common.Option
parsedOpts *common.Options
parsedOpts base.Options
}
func (c *LdapCommand) Synopsis() string {
@@ -77,7 +75,7 @@ func (c *LdapCommand) Flags() *base.FlagSets {
Usage: "The auth-method resource to use for the operation.",
})
if c.parsedOpts == nil || !c.parsedOpts.WithSkipScopeIdFlag {
if !c.parsedOpts.WithSkipScopeIdFlag {
f.StringVar(&base.StringVar{
Name: "scope-id",
EnvVar: "BOUNDARY_SCOPE_ID",
@@ -98,12 +96,7 @@ func (c *LdapCommand) AutocompleteFlags() complete.Flags {
}
func (c *LdapCommand) Run(args []string) int {
opts, err := common.GetOpts(c.Opts...)
if err != nil {
c.PrintCliError(err)
return base.CommandCliError
}
c.parsedOpts = opts
c.parsedOpts = base.GetOpts(c.Opts...)
f := c.Flags()
if err := f.Parse(args); err != nil {
@@ -193,5 +186,5 @@ func (c *LdapCommand) Run(args []string) int {
return base.CommandCliError
}
return saveAndOrPrintToken(c.Command, result)
return saveAndOrPrintToken(c.Command, result, c.Opts...)
}
+4 -12
View File
@@ -14,7 +14,6 @@ import (
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/authmethods"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/common"
"github.com/hashicorp/boundary/internal/types/scope"
"github.com/hashicorp/cap/util"
"github.com/mitchellh/cli"
@@ -30,8 +29,7 @@ var (
type OidcCommand struct {
*base.Command
Opts []common.Option
parsedOpts *common.Options
parsedOpts base.Options
}
func (c *OidcCommand) Synopsis() string {
@@ -61,7 +59,7 @@ func (c *OidcCommand) Flags() *base.FlagSets {
Usage: "The auth-method resource to use for the operation",
})
if c.parsedOpts == nil || !c.parsedOpts.WithSkipScopeIdFlag {
if !c.parsedOpts.WithSkipScopeIdFlag {
f.StringVar(&base.StringVar{
Name: "scope-id",
EnvVar: "BOUNDARY_SCOPE_ID",
@@ -81,13 +79,7 @@ func (c *OidcCommand) AutocompleteFlags() complete.Flags {
}
func (c *OidcCommand) Run(args []string) int {
opts, err := common.GetOpts(c.Opts...)
if err != nil {
c.PrintCliError(err)
return base.CommandCliError
}
c.parsedOpts = opts
c.parsedOpts = base.GetOpts(c.Opts...)
f := c.Flags()
if err := f.Parse(args); err != nil {
@@ -196,5 +188,5 @@ func (c *OidcCommand) Run(args []string) int {
return base.CommandCliError
}
return saveAndOrPrintToken(c.Command, result)
return saveAndOrPrintToken(c.Command, result, c.Opts...)
}
+4 -11
View File
@@ -13,7 +13,6 @@ import (
"github.com/hashicorp/boundary/api/authmethods"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/hashicorp/boundary/internal/cmd/common"
"github.com/hashicorp/go-secure-stdlib/parseutil"
"github.com/hashicorp/go-secure-stdlib/password"
"github.com/mitchellh/cli"
@@ -37,8 +36,7 @@ type PasswordCommand struct {
flagLoginName string
flagPassword string
Opts []common.Option
parsedOpts *common.Options
parsedOpts base.Options
}
func (c *PasswordCommand) Synopsis() string {
@@ -82,7 +80,7 @@ func (c *PasswordCommand) Flags() *base.FlagSets {
Usage: "The auth-method resource to use for the operation.",
})
if c.parsedOpts == nil || !c.parsedOpts.WithSkipScopeIdFlag {
if !c.parsedOpts.WithSkipScopeIdFlag {
f.StringVar(&base.StringVar{
Name: "scope-id",
EnvVar: "BOUNDARY_SCOPE_ID",
@@ -103,12 +101,7 @@ func (c *PasswordCommand) AutocompleteFlags() complete.Flags {
}
func (c *PasswordCommand) Run(args []string) int {
opts, err := common.GetOpts(c.Opts...)
if err != nil {
c.PrintCliError(err)
return base.CommandCliError
}
c.parsedOpts = opts
c.parsedOpts = base.GetOpts(c.Opts...)
f := c.Flags()
@@ -199,5 +192,5 @@ func (c *PasswordCommand) Run(args []string) int {
return base.CommandCliError
}
return saveAndOrPrintToken(c.Command, result)
return saveAndOrPrintToken(c.Command, result, c.Opts...)
}
-39
View File
@@ -1,39 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package common
// GetOpts - iterate the inbound Options and return a struct.
func GetOpts(opt ...Option) (*Options, error) {
opts := getDefaultOptions()
for _, o := range opt {
if o == nil {
continue
}
if err := o(opts); err != nil {
return nil, err
}
}
return opts, nil
}
// Option - how Options are passed as arguments.
type Option func(*Options) error
// Options - how Options are represented.
type Options struct {
WithSkipScopeIdFlag bool
}
func getDefaultOptions() *Options {
return &Options{}
}
// WithSkipScopeIdFlag tells a command to not create a scope ID flag (usually
// because it's already been defined)
func WithSkipScopeIdFlag(with bool) Option {
return func(o *Options) error {
o.WithSkipScopeIdFlag = with
return nil
}
}
-32
View File
@@ -1,32 +0,0 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package common
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Test_GetOpts provides unit tests for GetOpts and all the options
func Test_GetOpts(t *testing.T) {
t.Parallel()
t.Run("nil-options", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
opts, err := GetOpts(nil, nil)
require.NoError(err)
testOpts := getDefaultOptions()
assert.Equal(opts, testOpts)
})
t.Run("WithSkipScopeIdFlag", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
opts, err := GetOpts(WithSkipScopeIdFlag(true))
require.NoError(err)
testOpts := getDefaultOptions()
testOpts.WithSkipScopeIdFlag = true
assert.Equal(opts, testOpts)
})
}
+4 -4
View File
@@ -11,7 +11,7 @@ import (
)
func init() {
commandFactoryWrapper = daemonWrap
clientCacheWrapper = daemonWrap
extraCommandsFuncs = append(extraCommandsFuncs, func(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
delete(Commands, "daemon")
@@ -38,8 +38,8 @@ func init() {
})
}
// daemonWrap wraps the provided wrappableCommand with a daemon writer which
// daemonWrap wraps the provided cacheEnabledCommand with a daemon writer which
// conditionally starts the client side daemon after the command completes
func daemonWrap(ui cli.Ui, c wrappableCommand) cli.CommandFactory {
return daemon.Wrap(ui, c)
func daemonWrap(c cacheEnabledCommand) cli.CommandFactory {
return daemon.Wrap(c)
}
+4 -4
View File
@@ -13,7 +13,7 @@ import (
)
func init() {
commandFactoryWrapper = daemonWrap
clientCacheWrapper = daemonWrap
extraCommandsFuncs = append(extraCommandsFuncs, func(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
delete(Commands, "daemon")
@@ -40,8 +40,8 @@ func init() {
})
}
// daemonWrap wraps the provided wrappableCommand with a daemon writer which
// daemonWrap wraps the provided cacheEnabledCommand with a daemon writer which
// conditionally starts the client side daemon after the command completes
func daemonWrap(ui cli.Ui, c wrappableCommand) cli.CommandFactory {
return daemon.Wrap(ui, c)
func daemonWrap(c cacheEnabledCommand) cli.CommandFactory {
return daemon.Wrap(c)
}
+4 -4
View File
@@ -11,7 +11,7 @@ import (
)
func init() {
commandFactoryWrapper = daemonWrap
clientCacheWrapper = daemonWrap
extraCommandsFuncs = append(extraCommandsFuncs, func(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
delete(Commands, "daemon")
@@ -38,8 +38,8 @@ func init() {
})
}
// daemonWrap wraps the provided wrappableCommand with a daemon writer which
// daemonWrap wraps the provided cacheEnabledCommand with a daemon writer which
// conditionally starts the client side daemon after the command completes
func daemonWrap(ui cli.Ui, c wrappableCommand) cli.CommandFactory {
return daemon.Wrap(ui, c)
func daemonWrap(c cacheEnabledCommand) cli.CommandFactory {
return daemon.Wrap(c)
}
+4 -4
View File
@@ -13,7 +13,7 @@ import (
)
func init() {
commandFactoryWrapper = daemonWrap
clientCacheWrapper = daemonWrap
extraCommandsFuncs = append(extraCommandsFuncs, func(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
delete(Commands, "daemon")
@@ -40,8 +40,8 @@ func init() {
})
}
// daemonWrap wraps the provided wrappableCommand with a daemon writer which
// daemonWrap wraps the provided cacheEnabledCommand with a daemon writer which
// conditionally starts the client side daemon after the command completes
func daemonWrap(ui cli.Ui, c wrappableCommand) cli.CommandFactory {
return daemon.Wrap(ui, c)
func daemonWrap(c cacheEnabledCommand) cli.CommandFactory {
return daemon.Wrap(c)
}