From 003774b4275285a6de8b172fbabe69a04aa4a4e8 Mon Sep 17 00:00:00 2001 From: Haotian Zeng Date: Tue, 16 May 2023 20:06:04 -0700 Subject: [PATCH 1/6] change CLI output format of Secret: field in boundary connect --- internal/cmd/commands/connect/funcs.go | 62 +++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/internal/cmd/commands/connect/funcs.go b/internal/cmd/commands/connect/funcs.go index 4cdc92c69b..8f2f7f3d0c 100644 --- a/internal/cmd/commands/connect/funcs.go +++ b/internal/cmd/commands/connect/funcs.go @@ -4,7 +4,6 @@ package connect import ( - "bytes" "encoding/base64" "encoding/json" "fmt" @@ -13,6 +12,7 @@ import ( "github.com/hashicorp/boundary/api/targets" "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/mitchellh/mapstructure" ) func generateSessionInfoTableOutput(in SessionInfo) string { @@ -99,15 +99,63 @@ func fmtSecretForTable(indent int, sc *targets.SessionCredential) []string { if err != nil { return origSecret } - dst := new(bytes.Buffer) - if err := json.Indent(dst, in, fmt.Sprintf("%s ", prefixStr), fmt.Sprintf("%s ", prefixStr)); err != nil { + + // The following operations are performed to better format unspecified credential types. + // Credential values (with the exception of Vault metadata) are printed with no indentation + // so that X.509 certificates can be easily copied and pasted by the user. + var out []string + var baseCredMap map[string]any + mdMap := make(map[string]any) + err = json.Unmarshal(in, &baseCredMap) + if err != nil { return origSecret } - secretStr := strings.Split(dst.String(), "\n") - if len(secretStr) > 0 { - secretStr[0] = fmt.Sprintf("%s %s", prefixStr, secretStr[0]) + for k, iface := range baseCredMap { + + // Treat the Vault credential metadata as an exception case, to apply the better-looking + // base.WrapMap formatting. + if k == "metadata" { + md := make(map[string]any) + if err := mapstructure.Decode(iface, &md); err != nil { + // Return a best-effort representation of the metadata if it is + // unable to be decoded. + mdMap[k] = iface + continue + } + mdMap[k] = md + continue + } + + // Print all other credential fields with no spacing for the values + switch iface := iface.(type) { + case nil: + out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) + case map[string]string: + out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) + for kk, vv := range iface { + out = append(out, fmt.Sprintf("%s %s:", prefixStr, kk)) + out = append(out, fmt.Sprintf("%s\n", vv)) + } + case map[string]any: + // TODO: check if this is the only possible case for an 'unspecified' type credential + // and get rid of the other cases if this is so. + out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) + for kk, vv := range iface { + out = append(out, fmt.Sprintf("%s %s:", prefixStr, kk)) + out = append(out, fmt.Sprintf("%v\n", vv)) + } + default: + out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) + out = append(out, fmt.Sprintf("%v\n", iface)) + } } - return secretStr + + for k, v := range mdMap { + out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) + out = append(out, base.WrapMap(indent+6, 0, v.(map[string]any))) + } + + return out } func generateConnectionInfoTableOutput(in ConnectionInfo) string { From c6b9f05785374c1524cd6877cae3aee2f10bbd77 Mon Sep 17 00:00:00 2001 From: Haotian Zeng Date: Wed, 17 May 2023 14:04:53 -0700 Subject: [PATCH 2/6] use pem.Decode to avoid searching keys metadata edge case --- internal/cmd/commands/connect/funcs.go | 51 +++++++++----------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/internal/cmd/commands/connect/funcs.go b/internal/cmd/commands/connect/funcs.go index 8f2f7f3d0c..d14692decd 100644 --- a/internal/cmd/commands/connect/funcs.go +++ b/internal/cmd/commands/connect/funcs.go @@ -6,13 +6,13 @@ package connect import ( "encoding/base64" "encoding/json" + "encoding/pem" "fmt" "strings" "time" "github.com/hashicorp/boundary/api/targets" "github.com/hashicorp/boundary/internal/cmd/base" - "github.com/mitchellh/mapstructure" ) func generateSessionInfoTableOutput(in SessionInfo) string { @@ -101,60 +101,43 @@ func fmtSecretForTable(indent int, sc *targets.SessionCredential) []string { } // The following operations are performed to better format unspecified credential types. - // Credential values (with the exception of Vault metadata) are printed with no indentation - // so that X.509 certificates can be easily copied and pasted by the user. + // Credential values are printed with no indentation so that X.509 certificates can be + // easily copied and pasted by the user. var out []string var baseCredMap map[string]any - mdMap := make(map[string]any) err = json.Unmarshal(in, &baseCredMap) if err != nil { return origSecret } for k, iface := range baseCredMap { - - // Treat the Vault credential metadata as an exception case, to apply the better-looking - // base.WrapMap formatting. - if k == "metadata" { - md := make(map[string]any) - if err := mapstructure.Decode(iface, &md); err != nil { - // Return a best-effort representation of the metadata if it is - // unable to be decoded. - mdMap[k] = iface - continue - } - mdMap[k] = md - continue - } - - // Print all other credential fields with no spacing for the values switch iface := iface.(type) { case nil: out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) - case map[string]string: - out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) - for kk, vv := range iface { - out = append(out, fmt.Sprintf("%s %s:", prefixStr, kk)) - out = append(out, fmt.Sprintf("%s\n", vv)) - } case map[string]any: // TODO: check if this is the only possible case for an 'unspecified' type credential // and get rid of the other cases if this is so. out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) + certs := make(map[string]any, len(iface)) for kk, vv := range iface { - out = append(out, fmt.Sprintf("%s %s:", prefixStr, kk)) - out = append(out, fmt.Sprintf("%v\n", vv)) + if p, _ := pem.Decode([]byte(fmt.Sprintf("%v", vv))); p != nil { + // iface is a certificate. Print without indents. + certs[kk] = vv + } + } + for c := range certs { + delete(iface, c) + out = append(out, fmt.Sprintf("%s %s:", prefixStr, c)) + out = append(out, fmt.Sprintf("%v\n", certs[c])) + } + if len(iface) > 0 { + out = append(out, base.WrapMap(indent+6, 0, iface)) } default: out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) - out = append(out, fmt.Sprintf("%v\n", iface)) + out = append(out, fmt.Sprintf("%s %v\n", prefixStr, iface)) } } - for k, v := range mdMap { - out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) - out = append(out, base.WrapMap(indent+6, 0, v.(map[string]any))) - } - return out } From 154be5bc05e4bb620fcda1d6b1f1321ca4ed4a29 Mon Sep 17 00:00:00 2001 From: Haotian Zeng Date: Wed, 17 May 2023 15:47:58 -0700 Subject: [PATCH 3/6] fixup! use pem.Decode to avoid searching keys metadata edge case --- internal/cmd/commands/connect/funcs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/cmd/commands/connect/funcs.go b/internal/cmd/commands/connect/funcs.go index d14692decd..c1a509ff95 100644 --- a/internal/cmd/commands/connect/funcs.go +++ b/internal/cmd/commands/connect/funcs.go @@ -120,7 +120,7 @@ func fmtSecretForTable(indent int, sc *targets.SessionCredential) []string { certs := make(map[string]any, len(iface)) for kk, vv := range iface { if p, _ := pem.Decode([]byte(fmt.Sprintf("%v", vv))); p != nil { - // iface is a certificate. Print without indents. + // If the value is PEM formatted, add it to a map to be formatted without indents. certs[kk] = vv } } From 06542a85806e229f84042b1dfdb922d9ebd3c808 Mon Sep 17 00:00:00 2001 From: Haotian Zeng Date: Thu, 18 May 2023 14:18:18 -0700 Subject: [PATCH 4/6] try to retain original json.indent functionality for default switch case --- internal/cmd/commands/connect/funcs.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/internal/cmd/commands/connect/funcs.go b/internal/cmd/commands/connect/funcs.go index c1a509ff95..8e1b49b9d7 100644 --- a/internal/cmd/commands/connect/funcs.go +++ b/internal/cmd/commands/connect/funcs.go @@ -4,6 +4,7 @@ package connect import ( + "bytes" "encoding/base64" "encoding/json" "encoding/pem" @@ -134,7 +135,21 @@ func fmtSecretForTable(indent int, sc *targets.SessionCredential) []string { } default: out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) - out = append(out, fmt.Sprintf("%s %v\n", prefixStr, iface)) + data, err := json.Marshal(iface) + if err != nil { + out = append(out, fmt.Sprintf("%s %v\n", prefixStr, iface)) + continue + } + dst := new(bytes.Buffer) + if err := json.Indent(dst, data, fmt.Sprintf("%s ", prefixStr), fmt.Sprintf("%s ", prefixStr)); err != nil { + out = append(out, fmt.Sprintf("%s %v\n", prefixStr, iface)) + continue + } + secretStr := strings.Split(dst.String(), "\n") + if len(secretStr) > 0 { + secretStr[0] = fmt.Sprintf("%s %s", prefixStr, secretStr[0]) + } + return secretStr } } From c5385bce76e2143ac9788e781cabb1e9d3d4a25a Mon Sep 17 00:00:00 2001 From: Haotian Zeng Date: Thu, 25 May 2023 18:14:00 -0700 Subject: [PATCH 5/6] implement another type assertion --- internal/cmd/commands/connect/funcs.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/internal/cmd/commands/connect/funcs.go b/internal/cmd/commands/connect/funcs.go index 8e1b49b9d7..18996d41c4 100644 --- a/internal/cmd/commands/connect/funcs.go +++ b/internal/cmd/commands/connect/funcs.go @@ -115,14 +115,24 @@ func fmtSecretForTable(indent int, sc *targets.SessionCredential) []string { case nil: out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) case map[string]any: - // TODO: check if this is the only possible case for an 'unspecified' type credential - // and get rid of the other cases if this is so. out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) - certs := make(map[string]any, len(iface)) + certs := make(map[string]any) for kk, vv := range iface { - if p, _ := pem.Decode([]byte(fmt.Sprintf("%v", vv))); p != nil { - // If the value is PEM formatted, add it to a map to be formatted without indents. - certs[kk] = vv + // If the value is PEM formatted, add it to a map to be formatted without indents. + switch vv := vv.(type) { + case []byte: + fmt.Sprintf("________________iface: []byte]\n%v", vv) + if p, _ := pem.Decode(vv); p != nil { + certs[kk] = vv + } + case string: + fmt.Sprintf("________________iface: string\n%s", vv) + if p, _ := pem.Decode([]byte(fmt.Sprintf("%s", vv))); p != nil { + // If the value is PEM formatted, add it to a map to be formatted without indents. + certs[kk] = vv + } + default: + // Print out when base.WrapMap() is called on iface below. } } for c := range certs { @@ -131,7 +141,7 @@ func fmtSecretForTable(indent int, sc *targets.SessionCredential) []string { out = append(out, fmt.Sprintf("%v\n", certs[c])) } if len(iface) > 0 { - out = append(out, base.WrapMap(indent+6, 0, iface)) + out = append(out, base.WrapMap(indent+6, 12, iface)) } default: out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) From 46958865357fad3975e089df79f2473dfa685ca1 Mon Sep 17 00:00:00 2001 From: Haotian Zeng Date: Wed, 31 May 2023 14:28:44 -0700 Subject: [PATCH 6/6] order initial map and fixup second case statement --- internal/cmd/commands/connect/funcs.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/internal/cmd/commands/connect/funcs.go b/internal/cmd/commands/connect/funcs.go index 18996d41c4..e9cd0feec7 100644 --- a/internal/cmd/commands/connect/funcs.go +++ b/internal/cmd/commands/connect/funcs.go @@ -9,6 +9,7 @@ import ( "encoding/json" "encoding/pem" "fmt" + "sort" "strings" "time" @@ -110,10 +111,18 @@ func fmtSecretForTable(indent int, sc *targets.SessionCredential) []string { if err != nil { return origSecret } - for k, iface := range baseCredMap { - switch iface := iface.(type) { + sortedKeys := make([]string, len(baseCredMap)) + for k := range baseCredMap { + sortedKeys = append(sortedKeys, k) + } + sort.Strings(sortedKeys) + + for _, k := range sortedKeys { + switch iface := baseCredMap[k].(type) { case nil: - out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) + if len(k) > 0 { + out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) + } case map[string]any: out = append(out, fmt.Sprintf("%s %s:", prefixStr, k)) certs := make(map[string]any) @@ -121,13 +130,11 @@ func fmtSecretForTable(indent int, sc *targets.SessionCredential) []string { // If the value is PEM formatted, add it to a map to be formatted without indents. switch vv := vv.(type) { case []byte: - fmt.Sprintf("________________iface: []byte]\n%v", vv) if p, _ := pem.Decode(vv); p != nil { certs[kk] = vv } case string: - fmt.Sprintf("________________iface: string\n%s", vv) - if p, _ := pem.Decode([]byte(fmt.Sprintf("%s", vv))); p != nil { + if p, _ := pem.Decode([]byte(vv)); p != nil { // If the value is PEM formatted, add it to a map to be formatted without indents. certs[kk] = vv } @@ -141,7 +148,7 @@ func fmtSecretForTable(indent int, sc *targets.SessionCredential) []string { out = append(out, fmt.Sprintf("%v\n", certs[c])) } if len(iface) > 0 { - out = append(out, base.WrapMap(indent+6, 12, iface)) + out = append(out, base.WrapMap(indent+6, 16, iface)) } default: out = append(out, fmt.Sprintf("%s %s:", prefixStr, k))