This commit adds CLI support for addresses to be attached directly to a Target, for all current types of Target (ssh and tcp). It also clarifies the relevant documentation regarding the autogen of API option functionality.
833 lines
26 KiB
Go
833 lines
26 KiB
Go
package targetscmd
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hashicorp/boundary/api"
|
|
"github.com/hashicorp/boundary/api/scopes"
|
|
"github.com/hashicorp/boundary/api/targets"
|
|
"github.com/hashicorp/boundary/globals"
|
|
"github.com/hashicorp/boundary/internal/cmd/base"
|
|
"github.com/hashicorp/boundary/internal/credential"
|
|
"github.com/hashicorp/boundary/internal/types/scope"
|
|
"github.com/hashicorp/go-secure-stdlib/strutil"
|
|
"github.com/mitchellh/go-wordwrap"
|
|
"github.com/posener/complete"
|
|
)
|
|
|
|
func init() {
|
|
extraActionsFlagsMapFunc = extraActionsFlagsMapFuncImpl
|
|
extraSynopsisFunc = extraSynopsisFuncImpl
|
|
extraFlagsFunc = extraFlagsFuncImpl
|
|
extraFlagsHandlingFunc = extraFlagsHandlingFuncImpl
|
|
executeExtraActions = executeExtraActionsImpl
|
|
printCustomActionOutput = printCustomActionOutputImpl
|
|
}
|
|
|
|
type extraCmdVars struct {
|
|
flagHostSets []string
|
|
flagHostSources []string
|
|
flagBrokeredCredentialSources []string
|
|
flagInjectedApplicationCredentialSources []string
|
|
flagHostId string
|
|
sar *targets.SessionAuthorizationResult
|
|
}
|
|
|
|
func extraActionsFlagsMapFuncImpl() map[string][]string {
|
|
return map[string][]string{
|
|
"authorize-session": {"id", "host-id"},
|
|
"add-host-sources": {"id", "host-source", "version"},
|
|
"remove-host-sources": {"id", "host-source", "version"},
|
|
"set-host-sources": {"id", "host-source", "version"},
|
|
"add-credential-sources": {"id", "application-credential-source", "brokered-credential-source", "injected-application-credential-source", "version"},
|
|
"remove-credential-sources": {"id", "application-credential-source", "brokered-credential-source", "injected-application-credential-source", "version"},
|
|
"set-credential-sources": {"id", "application-credential-source", "brokered-credential-source", "injected-application-credential-source", "version"},
|
|
}
|
|
}
|
|
|
|
func extraSynopsisFuncImpl(c *Command) string {
|
|
switch c.Func {
|
|
case "add-host-sources", "set-host-sources", "remove-host-sources":
|
|
var in string
|
|
switch {
|
|
case strings.HasPrefix(c.Func, "add"):
|
|
in = "Add host sources to"
|
|
case strings.HasPrefix(c.Func, "set"):
|
|
in = "Set the full contents of the host sources on"
|
|
case strings.HasPrefix(c.Func, "remove"):
|
|
in = "Remove host sources from"
|
|
}
|
|
return wordwrap.WrapString(fmt.Sprintf("%s a target", in), base.TermWidth)
|
|
|
|
case "add-credential-sources", "set-credential-sources", "remove-credential-sources":
|
|
var in string
|
|
switch {
|
|
case strings.HasPrefix(c.Func, "add"):
|
|
in = "Add credential sources to"
|
|
case strings.HasPrefix(c.Func, "set"):
|
|
in = "Set the full contents of the credential sources on"
|
|
case strings.HasPrefix(c.Func, "remove"):
|
|
in = "Remove credential sources from"
|
|
}
|
|
return wordwrap.WrapString(fmt.Sprintf("%s a target", in), base.TermWidth)
|
|
|
|
case "authorize-session":
|
|
return "Request session authorization against the target"
|
|
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func (c *Command) extraHelpFunc(helpMap map[string]func() string) string {
|
|
var helpStr string
|
|
switch c.Func {
|
|
case "":
|
|
return base.WrapForHelpText([]string{
|
|
"Usage: boundary targets [sub command] [options] [args]",
|
|
"",
|
|
" This command allows operations on Boundary target resources. Example:",
|
|
"",
|
|
" Read a target:",
|
|
"",
|
|
` $ boundary targets read -id ttcp_1234567890`,
|
|
"",
|
|
" Please see the targets subcommand help for detailed usage information.",
|
|
})
|
|
case "create":
|
|
helpStr = base.WrapForHelpText([]string{
|
|
"Usage: boundary targets create [type] [sub command] [options] [args]",
|
|
"",
|
|
" This command allows create operations on Boundary target resources. Example:",
|
|
"",
|
|
" Create a tcp-type target:",
|
|
"",
|
|
` $ boundary targets create tcp -name prodops -description "For ProdOps usage"`,
|
|
"",
|
|
" Please see the typed subcommand help for detailed usage information.",
|
|
})
|
|
case "update":
|
|
helpStr = base.WrapForHelpText([]string{
|
|
"Usage: boundary targets update [type] [sub command] [options] [args]",
|
|
"",
|
|
" This command allows update operations on Boundary target resources. Example:",
|
|
"",
|
|
" Update a tcp-type target:",
|
|
"",
|
|
` $ boundary targets update tcp -id ttcp_1234567890 -name devops -description "For DevOps usage"`,
|
|
"",
|
|
" Please see the typed subcommand help for detailed usage information.",
|
|
})
|
|
case "add-host-sources":
|
|
helpStr = base.WrapForHelpText([]string{
|
|
"Usage: boundary target add-host-sources [options] [args]",
|
|
"",
|
|
" This command allows adding host sources to target resources. Example:",
|
|
"",
|
|
" Add host sources to a tcp-type target:",
|
|
"",
|
|
` $ boundary targets add-host-sources -id ttcp_1234567890 -host-source hsst_1234567890 -host-source hsst_0987654321`,
|
|
"",
|
|
"",
|
|
})
|
|
case "remove-host-sources":
|
|
helpStr = base.WrapForHelpText([]string{
|
|
"Usage: boundary target remove-host-sources [options] [args]",
|
|
"",
|
|
" This command allows removing host sources from target resources. Example:",
|
|
"",
|
|
" Remove host sources from a tcp-type target:",
|
|
"",
|
|
` $ boundary targets remove-host-sources -id ttcp_1234567890 -host-source hsst_1234567890 -host-source hsst_0987654321`,
|
|
"",
|
|
"",
|
|
})
|
|
case "set-host-sources":
|
|
helpStr = base.WrapForHelpText([]string{
|
|
"Usage: boundary target set-host-sources [options] [args]",
|
|
"",
|
|
" This command allows setting the complete set of host sources on a target resource. Example:",
|
|
"",
|
|
" Set host sources on a tcp-type target:",
|
|
"",
|
|
` $ boundary targets set-host-sources -id ttcp_1234567890 -host-source hsst_1234567890`,
|
|
"",
|
|
"",
|
|
})
|
|
case "add-credential-sources":
|
|
helpStr = base.WrapForHelpText([]string{
|
|
"Usage: boundary target add-credential-sources [options] [args]",
|
|
"",
|
|
" This command allows adding credential sources to target resources. Example:",
|
|
"",
|
|
" Add credential sources to a tcp-type target:",
|
|
"",
|
|
` $ boundary targets add-credential-sources -id ttcp_1234567890 -brokered-credential-source clvlt_1234567890 -brokered-credential-source clvlt_0987654321`,
|
|
"",
|
|
"",
|
|
})
|
|
case "remove-credential-sources":
|
|
helpStr = base.WrapForHelpText([]string{
|
|
"Usage: boundary target remove-credential-sources [options] [args]",
|
|
"",
|
|
" This command allows removing credential sources from target resources. Example:",
|
|
"",
|
|
" Remove credential sources from a tcp-type target:",
|
|
"",
|
|
` $ boundary targets remove-credential-sources -id ttcp_1234567890 -brokered-credential-source clvlt_1234567890 -brokered-credential-source clvlt_0987654321`,
|
|
"",
|
|
"",
|
|
})
|
|
case "set-credential-sources":
|
|
helpStr = base.WrapForHelpText([]string{
|
|
"Usage: boundary target set-credential-sources [options] [args]",
|
|
"",
|
|
" This command allows setting the complete set of credential sources on a target resource. Example:",
|
|
"",
|
|
" Set credential sources on a tcp-type target:",
|
|
"",
|
|
` $ boundary targets set-credential-sources -id ttcp_1234567890 -brokered-credential-source clvlt_1234567890`,
|
|
"",
|
|
"",
|
|
})
|
|
case "authorize-session":
|
|
helpStr = base.WrapForHelpText([]string{
|
|
"Usage: boundary target authorize-session [options] [args]",
|
|
"",
|
|
" This command allows fetching session authorization credentials against a target. Example:",
|
|
"",
|
|
" Request an authorized session using the target ID:",
|
|
"",
|
|
` $ boundary targets authorize-session -id ttcp_1234567890`,
|
|
"",
|
|
" Request an authorized session using the scope ID and target name:",
|
|
"",
|
|
` $ boundary targets authorize-session -scope-id o_1234567890 -name prod-ssh`,
|
|
"",
|
|
"",
|
|
})
|
|
}
|
|
return helpStr + c.Flags().Help()
|
|
}
|
|
|
|
func extraFlagsFuncImpl(c *Command, _ *base.FlagSets, f *base.FlagSet) {
|
|
for _, name := range flagsMap[c.Func] {
|
|
switch name {
|
|
case "host-set":
|
|
f.StringSliceVar(&base.StringSliceVar{
|
|
Name: "host-set",
|
|
Target: &c.flagHostSets,
|
|
Usage: "The host-set resources to add, remove, or set. May be specified multiple times.",
|
|
})
|
|
case "host-source":
|
|
f.StringSliceVar(&base.StringSliceVar{
|
|
Name: "host-source",
|
|
Target: &c.flagHostSources,
|
|
Usage: "The host sources to add, remove, or set. May be specified multiple times.",
|
|
})
|
|
case "host-id":
|
|
f.StringVar(&base.StringVar{
|
|
Name: "host-id",
|
|
Target: &c.flagHostId,
|
|
Usage: "The ID of a specific host to connect to out of the hosts from the target's host sets. If not specified, one is chosen at random.",
|
|
})
|
|
case "brokered-credential-source":
|
|
f.StringSliceVar(&base.StringSliceVar{
|
|
Name: "brokered-credential-source",
|
|
Target: &c.flagBrokeredCredentialSources,
|
|
Usage: "The credential source to add, set, or remove that Boundary will return to the user when creating a connection. May be specified multiple times.",
|
|
})
|
|
case "application-credential-source":
|
|
f.StringSliceVar(&base.StringSliceVar{
|
|
Name: "application-credential-source",
|
|
Target: &c.flagBrokeredCredentialSources,
|
|
Usage: "Deprecated: use -brokered-credential-source instead",
|
|
})
|
|
case "injected-application-credential-source":
|
|
f.StringSliceVar(&base.StringSliceVar{
|
|
Name: "injected-application-credential-source",
|
|
Target: &c.flagInjectedApplicationCredentialSources,
|
|
Usage: "The credential source to add, set, or remove that Boundary will inject when creating a connection. May be specified multiple times.",
|
|
})
|
|
}
|
|
}
|
|
|
|
if c.Func == "authorize-session" {
|
|
flagsMap[c.Func] = append(flagsMap[c.Func], "name", "scope-id", "scope-name")
|
|
|
|
// We put these here to change usage and change defaults (don't want
|
|
// them populated by default). Otherwise the common flags function will
|
|
// populate these values, and they can't be changed after-the-fact.
|
|
f.StringVar(&base.StringVar{
|
|
Name: "name",
|
|
Target: &c.FlagName,
|
|
Usage: "Target name, if authorizing the session via scope parameters and target name.",
|
|
})
|
|
|
|
f.StringVar(&base.StringVar{
|
|
Name: "scope-id",
|
|
Target: &c.FlagScopeId,
|
|
EnvVar: "BOUNDARY_SCOPE_ID",
|
|
Completion: complete.PredictAnything,
|
|
Usage: "Target scope ID, if authorizing the session via scope parameters and target name. Mutually exclusive with -scope-name.",
|
|
})
|
|
|
|
f.StringVar(&base.StringVar{
|
|
Name: "scope-name",
|
|
Target: &c.FlagScopeName,
|
|
EnvVar: "BOUNDARY_SCOPE_NAME",
|
|
Completion: complete.PredictAnything,
|
|
Usage: "Target scope name, if authorizing the session via scope parameters and target name. Mutually exclusive with -scope-id.",
|
|
})
|
|
}
|
|
}
|
|
|
|
func extraFlagsHandlingFuncImpl(c *Command, _ *base.FlagSets, opts *[]targets.Option) bool {
|
|
// This is custom logic because of the authorized-session handling. If we
|
|
// support all resources to be looked up by name + scope info we can
|
|
// eventually graduate this out to the main template.
|
|
if strutil.StrListContains(flagsMap[c.Func], "id") {
|
|
switch c.Func {
|
|
case "authorize-session":
|
|
if c.FlagId == "" &&
|
|
(c.FlagName == "" ||
|
|
(c.FlagScopeId == "" && c.FlagScopeName == "")) {
|
|
c.UI.Error("ID was not passed in, but no combination of name and scope ID/name was passed in either")
|
|
return false
|
|
}
|
|
if c.FlagId != "" &&
|
|
(c.FlagName != "" || c.FlagScopeId != "" || c.FlagScopeName != "") {
|
|
c.UI.Error("Cannot specify a target ID and also other lookup parameters")
|
|
return false
|
|
}
|
|
default:
|
|
if c.FlagId == "" {
|
|
c.UI.Error("ID is required but not passed in via -id")
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
if strutil.StrListContains(flagsMap[c.Func], "scope-id") && c.FlagScopeId != "" {
|
|
*opts = append(*opts, targets.WithScopeId(c.FlagScopeId))
|
|
}
|
|
|
|
switch c.Func {
|
|
case "add-host-sources", "remove-host-sources":
|
|
if len(c.flagHostSources) == 0 {
|
|
c.UI.Error("No host sources supplied via -host-source")
|
|
return false
|
|
}
|
|
|
|
case "set-host-sources":
|
|
switch len(c.flagHostSources) {
|
|
case 0:
|
|
c.UI.Error("No host sources supplied via -host-source")
|
|
return false
|
|
case 1:
|
|
if c.flagHostSources[0] == "null" {
|
|
c.flagHostSources = nil
|
|
}
|
|
}
|
|
|
|
case "add-credential-sources", "remove-credential-sources":
|
|
if len(c.flagBrokeredCredentialSources)+len(c.flagInjectedApplicationCredentialSources) == 0 {
|
|
c.UI.Error("No credential sources supplied via -brokered-credential-source or -injected-application-credential-source")
|
|
return false
|
|
}
|
|
|
|
if len(c.flagBrokeredCredentialSources) > 0 {
|
|
*opts = append(*opts, targets.WithBrokeredCredentialSourceIds(c.flagBrokeredCredentialSources))
|
|
}
|
|
if len(c.flagInjectedApplicationCredentialSources) > 0 {
|
|
*opts = append(*opts, targets.WithInjectedApplicationCredentialSourceIds(c.flagInjectedApplicationCredentialSources))
|
|
}
|
|
|
|
case "set-credential-sources":
|
|
if len(c.flagBrokeredCredentialSources)+len(c.flagInjectedApplicationCredentialSources) == 0 {
|
|
c.UI.Error("No credential sources supplied via -brokered-credential-source or -injected-application-credential-source")
|
|
return false
|
|
}
|
|
|
|
switch len(c.flagBrokeredCredentialSources) {
|
|
case 0:
|
|
// do nothing
|
|
case 1:
|
|
if c.flagBrokeredCredentialSources[0] == "null" {
|
|
*opts = append(*opts, targets.DefaultBrokeredCredentialSourceIds())
|
|
break
|
|
}
|
|
fallthrough
|
|
default:
|
|
*opts = append(*opts, targets.WithBrokeredCredentialSourceIds(c.flagBrokeredCredentialSources))
|
|
}
|
|
switch len(c.flagInjectedApplicationCredentialSources) {
|
|
case 0:
|
|
// do nothing
|
|
case 1:
|
|
if c.flagInjectedApplicationCredentialSources[0] == "null" {
|
|
*opts = append(*opts, targets.DefaultInjectedApplicationCredentialSourceIds())
|
|
break
|
|
}
|
|
fallthrough
|
|
default:
|
|
*opts = append(*opts, targets.WithInjectedApplicationCredentialSourceIds(c.flagInjectedApplicationCredentialSources))
|
|
}
|
|
|
|
case "authorize-session":
|
|
if len(c.flagHostId) != 0 {
|
|
*opts = append(*opts, targets.WithHostId(c.flagHostId))
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func executeExtraActionsImpl(c *Command, origResp *api.Response, origItem *targets.Target, origItems []*targets.Target, origError error, targetClient *targets.Client, version uint32, opts []targets.Option) (*api.Response, *targets.Target, []*targets.Target, error) {
|
|
switch c.Func {
|
|
case "add-host-sources":
|
|
result, err := targetClient.AddHostSources(c.Context, c.FlagId, version, c.flagHostSources, opts...)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
return result.GetResponse(), result.GetItem(), nil, err
|
|
case "remove-host-sources":
|
|
result, err := targetClient.RemoveHostSources(c.Context, c.FlagId, version, c.flagHostSources, opts...)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
return result.GetResponse(), result.GetItem(), nil, err
|
|
case "set-host-sources":
|
|
result, err := targetClient.SetHostSources(c.Context, c.FlagId, version, c.flagHostSources, opts...)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
return result.GetResponse(), result.GetItem(), nil, err
|
|
case "add-credential-sources":
|
|
result, err := targetClient.AddCredentialSources(c.Context, c.FlagId, version, opts...)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
return result.GetResponse(), result.GetItem(), nil, err
|
|
case "remove-credential-sources":
|
|
result, err := targetClient.RemoveCredentialSources(c.Context, c.FlagId, version, opts...)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
return result.GetResponse(), result.GetItem(), nil, err
|
|
case "set-credential-sources":
|
|
result, err := targetClient.SetCredentialSources(c.Context, c.FlagId, version, opts...)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
return result.GetResponse(), result.GetItem(), nil, err
|
|
case "authorize-session":
|
|
var err error
|
|
c.plural = "a session against target"
|
|
c.sar, err = targetClient.AuthorizeSession(c.Context, c.FlagId, opts...)
|
|
return nil, nil, nil, err
|
|
}
|
|
return origResp, origItem, origItems, origError
|
|
}
|
|
|
|
func (c *Command) printListTable(items []*targets.Target) string {
|
|
if len(items) == 0 {
|
|
return "No targets found"
|
|
}
|
|
var output []string
|
|
output = []string{
|
|
"",
|
|
"Target information:",
|
|
}
|
|
for i, item := range items {
|
|
if i > 0 {
|
|
output = append(output, "")
|
|
}
|
|
if item.Id != "" {
|
|
output = append(output,
|
|
fmt.Sprintf(" ID: %s", item.Id),
|
|
)
|
|
} else {
|
|
output = append(output,
|
|
fmt.Sprintf(" ID: %s", "(not available)"),
|
|
)
|
|
}
|
|
if c.FlagRecursive && item.ScopeId != "" {
|
|
output = append(output,
|
|
fmt.Sprintf(" Scope ID: %s", item.ScopeId),
|
|
)
|
|
}
|
|
if item.Version > 0 {
|
|
output = append(output,
|
|
fmt.Sprintf(" Version: %d", item.Version),
|
|
)
|
|
}
|
|
if item.Type != "" {
|
|
output = append(output,
|
|
fmt.Sprintf(" Type: %s", item.Type),
|
|
)
|
|
}
|
|
if item.Name != "" {
|
|
output = append(output,
|
|
fmt.Sprintf(" Name: %s", item.Name),
|
|
)
|
|
}
|
|
if item.Description != "" {
|
|
output = append(output,
|
|
fmt.Sprintf(" Description: %s", item.Description),
|
|
)
|
|
}
|
|
if item.Address != "" {
|
|
output = append(output,
|
|
fmt.Sprintf(" Address: %s", item.Address),
|
|
)
|
|
}
|
|
if len(item.AuthorizedActions) > 0 {
|
|
output = append(output,
|
|
" Authorized Actions:",
|
|
base.WrapSlice(6, item.AuthorizedActions),
|
|
)
|
|
}
|
|
}
|
|
|
|
return base.WrapForHelpText(output)
|
|
}
|
|
|
|
func printItemTable(item *targets.Target, resp *api.Response) string {
|
|
nonAttributeMap := map[string]any{}
|
|
if item.Id != "" {
|
|
nonAttributeMap["ID"] = item.Id
|
|
}
|
|
if item.Version != 0 {
|
|
nonAttributeMap["Version"] = item.Version
|
|
}
|
|
if item.Type != "" {
|
|
nonAttributeMap["Type"] = item.Type
|
|
}
|
|
if !item.CreatedTime.IsZero() {
|
|
nonAttributeMap["Created Time"] = item.CreatedTime.Local().Format(time.RFC1123)
|
|
}
|
|
if !item.UpdatedTime.IsZero() {
|
|
nonAttributeMap["Updated Time"] = item.UpdatedTime.Local().Format(time.RFC1123)
|
|
}
|
|
if item.Name != "" {
|
|
nonAttributeMap["Name"] = item.Name
|
|
}
|
|
if item.Description != "" {
|
|
nonAttributeMap["Description"] = item.Description
|
|
}
|
|
if item.Address != "" {
|
|
nonAttributeMap["Address"] = item.Address
|
|
}
|
|
if item.WorkerFilter != "" {
|
|
nonAttributeMap["Worker Filter"] = item.WorkerFilter
|
|
}
|
|
if item.EgressWorkerFilter != "" {
|
|
nonAttributeMap["Egress Worker Filter"] = item.EgressWorkerFilter
|
|
}
|
|
if item.IngressWorkerFilter != "" {
|
|
nonAttributeMap["Ingress Worker Filter"] = item.IngressWorkerFilter
|
|
}
|
|
if resp != nil && resp.Map != nil {
|
|
if resp.Map[globals.SessionConnectionLimitField] != nil {
|
|
nonAttributeMap["Session Connection Limit"] = item.SessionConnectionLimit
|
|
}
|
|
if resp.Map[globals.SessionMaxSecondsField] != nil {
|
|
nonAttributeMap["Session Max Seconds"] = item.SessionMaxSeconds
|
|
}
|
|
}
|
|
|
|
maxLength := base.MaxAttributesLength(nonAttributeMap, item.Attributes, keySubstMap)
|
|
|
|
var hostSourceMaps []map[string]any
|
|
if len(item.HostSources) > 0 {
|
|
for _, set := range item.HostSources {
|
|
m := map[string]any{
|
|
"ID": set.Id,
|
|
"Host Catalog ID": set.HostCatalogId,
|
|
}
|
|
hostSourceMaps = append(hostSourceMaps, m)
|
|
}
|
|
if l := len("Host Catalog ID"); l > maxLength {
|
|
maxLength = l
|
|
}
|
|
}
|
|
|
|
var credentialSourceMaps map[credential.Purpose][]map[string]any
|
|
if len(item.BrokeredCredentialSources) > 0 {
|
|
if credentialSourceMaps == nil {
|
|
credentialSourceMaps = make(map[credential.Purpose][]map[string]any)
|
|
}
|
|
var brokeredCredentialSourceMaps []map[string]any
|
|
for _, source := range item.BrokeredCredentialSources {
|
|
m := map[string]any{
|
|
"ID": source.Id,
|
|
"Credential Store ID": source.CredentialStoreId,
|
|
}
|
|
brokeredCredentialSourceMaps = append(brokeredCredentialSourceMaps, m)
|
|
}
|
|
credentialSourceMaps[credential.BrokeredPurpose] = brokeredCredentialSourceMaps
|
|
if l := len("Credential Store ID"); l > maxLength {
|
|
maxLength = l
|
|
}
|
|
}
|
|
if len(item.InjectedApplicationCredentialSources) > 0 {
|
|
if credentialSourceMaps == nil {
|
|
credentialSourceMaps = make(map[credential.Purpose][]map[string]any)
|
|
}
|
|
var injectedApplicationCredentialSourceMaps []map[string]any
|
|
for _, source := range item.InjectedApplicationCredentialSources {
|
|
m := map[string]any{
|
|
"ID": source.Id,
|
|
"Credential Store ID": source.CredentialStoreId,
|
|
}
|
|
injectedApplicationCredentialSourceMaps = append(injectedApplicationCredentialSourceMaps, m)
|
|
}
|
|
credentialSourceMaps[credential.InjectedApplicationPurpose] = injectedApplicationCredentialSourceMaps
|
|
if l := len("Credential Store ID"); l > maxLength {
|
|
maxLength = l
|
|
}
|
|
}
|
|
|
|
ret := []string{
|
|
"",
|
|
"Target information:",
|
|
base.WrapMap(2, maxLength+2, nonAttributeMap),
|
|
}
|
|
|
|
if item.Scope != nil {
|
|
ret = append(ret,
|
|
"",
|
|
" Scope:",
|
|
base.ScopeInfoForOutput(item.Scope, maxLength),
|
|
)
|
|
}
|
|
|
|
if len(item.AuthorizedActions) > 0 {
|
|
ret = append(ret,
|
|
"",
|
|
" Authorized Actions:",
|
|
base.WrapSlice(4, item.AuthorizedActions),
|
|
)
|
|
}
|
|
|
|
ret = append(ret,
|
|
"",
|
|
)
|
|
|
|
if len(hostSourceMaps) > 0 {
|
|
ret = append(ret,
|
|
" Host Sources:",
|
|
)
|
|
for _, m := range hostSourceMaps {
|
|
ret = append(ret,
|
|
base.WrapMap(4, maxLength, m),
|
|
"",
|
|
)
|
|
}
|
|
}
|
|
|
|
for purpose, sources := range credentialSourceMaps {
|
|
switch purpose {
|
|
case credential.BrokeredPurpose:
|
|
ret = append(ret,
|
|
" Brokered Credential Sources:",
|
|
)
|
|
case credential.InjectedApplicationPurpose:
|
|
ret = append(ret,
|
|
" Injected Application Credential Sources:",
|
|
)
|
|
}
|
|
for _, m := range sources {
|
|
ret = append(ret,
|
|
base.WrapMap(4, maxLength, m),
|
|
"",
|
|
)
|
|
}
|
|
}
|
|
|
|
if len(item.Attributes) > 0 {
|
|
ret = append(ret,
|
|
" Attributes:",
|
|
base.WrapMap(4, maxLength, item.Attributes),
|
|
)
|
|
}
|
|
|
|
return base.WrapForHelpText(ret)
|
|
}
|
|
|
|
func printCustomActionOutputImpl(c *Command) (bool, error) {
|
|
switch c.Func {
|
|
case "authorize-session":
|
|
item := c.sar.GetItem().(*targets.SessionAuthorization)
|
|
|
|
switch base.Format(c.UI) {
|
|
case "table":
|
|
var ret []string
|
|
|
|
nonAttributeMap := map[string]any{
|
|
"Session ID": item.SessionId,
|
|
"Target ID": item.TargetId,
|
|
"Scope ID": item.Scope.Id,
|
|
"User ID": item.UserId,
|
|
"Host ID": item.HostId,
|
|
"Endpoint": item.Endpoint,
|
|
"Created Time": item.CreatedTime.Local().Format(time.RFC1123),
|
|
"Type": item.Type,
|
|
"Authorization Token": item.AuthorizationToken,
|
|
}
|
|
|
|
maxLength := 0
|
|
for k := range nonAttributeMap {
|
|
if len(k) > maxLength {
|
|
maxLength = len(k)
|
|
}
|
|
}
|
|
|
|
ret = append(ret, "", "Target information:")
|
|
ret = append(ret,
|
|
// We do +2 because there is another +2 offset for credentials below
|
|
base.WrapMap(2, maxLength+2, nonAttributeMap),
|
|
)
|
|
|
|
ret = append(ret,
|
|
"",
|
|
)
|
|
if len(item.Credentials) > 0 {
|
|
ret = append(ret,
|
|
" Credentials:",
|
|
)
|
|
|
|
for _, cred := range item.Credentials {
|
|
if cred.Secret == nil || len(cred.Secret.Raw) == 0 {
|
|
continue
|
|
}
|
|
|
|
ret = append(ret,
|
|
fmt.Sprintf(" Credential Store ID: %s", cred.CredentialSource.CredentialStoreId),
|
|
fmt.Sprintf(" Credential Source ID: %s", cred.CredentialSource.Id),
|
|
fmt.Sprintf(" Credential Source Type: %s", cred.CredentialSource.Type))
|
|
|
|
if len(cred.CredentialSource.Name) > 0 {
|
|
ret = append(ret,
|
|
fmt.Sprintf(" Credential Source Name: %s", cred.CredentialSource.Name))
|
|
}
|
|
if len(cred.CredentialSource.Description) > 0 {
|
|
ret = append(ret,
|
|
fmt.Sprintf(" Credential Source Description: %s", cred.CredentialSource.Description))
|
|
}
|
|
if cred.CredentialSource.CredentialType != "" {
|
|
ret = append(ret,
|
|
fmt.Sprintf(" Credential Type: %s", cred.CredentialSource.CredentialType))
|
|
}
|
|
|
|
var secretStr []string
|
|
switch cred.CredentialSource.Type {
|
|
case "vault", "static":
|
|
switch {
|
|
case cred.Credential != nil:
|
|
maxLength := 0
|
|
for k := range cred.Credential {
|
|
if len(k) > maxLength {
|
|
maxLength = len(k)
|
|
}
|
|
}
|
|
secretStr = []string{fmt.Sprintf(" %s", base.WrapMap(2, maxLength+2, cred.Credential))}
|
|
|
|
default:
|
|
// If it's Vault, the result will be JSON, except in
|
|
// specific circumstances that aren't used for
|
|
// credential fetching. So we can take the bytes
|
|
// as-is (after base64-decoding), but we'll format
|
|
// it nicely.
|
|
in, err := base64.StdEncoding.DecodeString(strings.Trim(string(cred.Secret.Raw), `"`))
|
|
if err != nil {
|
|
return false, fmt.Errorf("Error decoding secret as base64: %w", err)
|
|
}
|
|
dst := new(bytes.Buffer)
|
|
if err := json.Indent(dst, in, " ", " "); err != nil {
|
|
return false, fmt.Errorf("Error pretty-printing JSON: %w", err)
|
|
}
|
|
secretStr = strings.Split(dst.String(), "\n")
|
|
if len(secretStr) > 0 {
|
|
// Indent doesn't apply to the first line 🙄
|
|
secretStr[0] = fmt.Sprintf(" %s", secretStr[0])
|
|
}
|
|
}
|
|
default:
|
|
// If it's not Vault, and not another known type,
|
|
// print out the base64-encoded value and leave it
|
|
// to the user to sort out.
|
|
secretStr = []string{fmt.Sprintf(" %s", secretStr)}
|
|
}
|
|
ret = append(ret, " Secret:")
|
|
ret = append(ret, secretStr...)
|
|
ret = append(ret, "")
|
|
}
|
|
}
|
|
|
|
c.UI.Output(base.WrapForHelpText(ret))
|
|
return true, nil
|
|
|
|
case "json":
|
|
if ok := c.PrintJsonItem(c.sar.GetResponse()); !ok {
|
|
return false, fmt.Errorf("Error formatting as JSON")
|
|
}
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
var keySubstMap = map[string]string{
|
|
"default_port": "Default Port",
|
|
}
|
|
|
|
func exampleOutput() string {
|
|
item := &targets.Target{
|
|
Id: "ttcp_1234567890",
|
|
ScopeId: scope.Global.String(),
|
|
Scope: &scopes.ScopeInfo{
|
|
Id: scope.Global.String(),
|
|
},
|
|
Name: "foo",
|
|
Description: "The bar of foos",
|
|
CreatedTime: time.Now().Add(-5 * time.Minute),
|
|
UpdatedTime: time.Now(),
|
|
Version: 3,
|
|
Type: "tcp",
|
|
HostSourceIds: []string{"hsst_1234567890", "hsst_0987654321"},
|
|
HostSources: []*targets.HostSource{
|
|
{
|
|
Id: "hsst_1234567890",
|
|
HostCatalogId: "hcst_1234567890",
|
|
},
|
|
{
|
|
Id: "hsst_0987654321",
|
|
HostCatalogId: "hcst_1234567890",
|
|
},
|
|
},
|
|
BrokeredCredentialSourceIds: []string{"clvlt_1234567890", "clvlt_0987654321"},
|
|
BrokeredCredentialSources: []*targets.CredentialSource{
|
|
{
|
|
Id: "clvlt_1234567890",
|
|
CredentialStoreId: "csvlt_1234567890",
|
|
},
|
|
{
|
|
Id: "clvlt_098765421",
|
|
CredentialStoreId: "clvlt_0987654321",
|
|
},
|
|
},
|
|
Attributes: map[string]any{
|
|
"default_port": 22,
|
|
},
|
|
}
|
|
return printItemTable(item, nil)
|
|
}
|