diff --git a/changelog/18463.txt b/changelog/18463.txt new file mode 100644 index 000000000..538f66eb1 --- /dev/null +++ b/changelog/18463.txt @@ -0,0 +1,3 @@ +```release-note:improvement +cli/pki: Add List-Intermediates functionality to pki client. +``` diff --git a/command/commands.go b/command/commands.go index 4432a6fbc..a7761c859 100644 --- a/command/commands.go +++ b/command/commands.go @@ -543,6 +543,11 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { BaseCommand: getBaseCommand(), }, nil }, + "pki list-intermediates": func() (cli.Command, error) { + return &PKIListChildrenCommand{ + BaseCommand: getBaseCommand(), + }, nil + }, "pki verify-sign": func() (cli.Command, error) { return &PKIVerifySignCommand{ BaseCommand: getBaseCommand(), diff --git a/command/pki_list_children_command.go b/command/pki_list_children_command.go new file mode 100644 index 000000000..23a390a7a --- /dev/null +++ b/command/pki_list_children_command.go @@ -0,0 +1,288 @@ +package command + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" + + "github.com/hashicorp/vault/api" + + "github.com/ghodss/yaml" + "github.com/ryanuber/columnize" +) + +type PKIListChildrenCommand struct { + *BaseCommand + + flagConfig string + flagReturnIndicator string + flagDefaultDisabled bool + flagList bool + + flagUseNames bool + + flagSignatureMatch bool + flagIndirectSignMatch bool + flagKeyIdMatch bool + flagSubjectMatch bool + flagPathMatch bool +} + +func (c *PKIListChildrenCommand) Synopsis() string { + return "Determine Which (of a List) of Certificates Were Issued by A Given Parent Certificate" +} + +func (c *PKIListChildrenCommand) Help() string { + helpText := ` +Usage: vault pki list-intermediates PARENT [CHILD] [CHILD] [CHILD] ... +PARENT is the certificate that might be the issuer that everything should be verified against. +CHILD is a list of paths to certificates to be compared to the PARENT, or pki mounts to look for certificates on. +If CHILD is omitted entirely, the list will be constructed from all accessible pki mounts. +This returns a list of issuing certificates, and whether they are a match. +By default, the type of match required is whether the PARENT has the expected subject, key_id, and could have (directly) +signed this issuer. The match criteria can be updated by changed the corresponding flag. +` + return strings.TrimSpace(helpText) +} + +func (c *PKIListChildrenCommand) Flags() *FlagSets { + set := c.flagSet(FlagSetHTTP | FlagSetOutputFormat) + f := set.NewFlagSet("Command Options") + + f.BoolVar(&BoolVar{ + Name: "subject_match", + Target: &c.flagSubjectMatch, + Default: true, + EnvVar: "", + Usage: `Whether the subject name of the potential parent cert matches the issuer name of the child cert`, + }) + + f.BoolVar(&BoolVar{ + Name: "key_id_match", + Target: &c.flagKeyIdMatch, + Default: true, + EnvVar: "", + Usage: `Whether the subject key_id of the potential parent cert matches the issuing key id of the child cert`, + }) + + f.BoolVar(&BoolVar{ + Name: "path_match", + Target: &c.flagPathMatch, + Default: false, + EnvVar: "", + Usage: `Whether the potential parent appears in the certificate chain of the issued cert`, + }) + + f.BoolVar(&BoolVar{ + Name: "direct_sign", + Target: &c.flagSignatureMatch, + Default: true, + EnvVar: "", + Usage: `Whether the key of the potential parent signed this issued certificate`, + }) + + f.BoolVar(&BoolVar{ + Name: "indirect_sign", + Target: &c.flagIndirectSignMatch, + Default: true, + EnvVar: "", + Usage: `Whether trusting the parent certificate is sufficient to trust the child certificate`, + }) + + f.BoolVar(&BoolVar{ + Name: "use_names", + Target: &c.flagUseNames, + Default: false, + EnvVar: "", + Usage: `Whether the list of issuers returned is referred to by name when it exists rather than uuid`, + }) + + return set +} + +func (c *PKIListChildrenCommand) Run(args []string) int { + f := c.Flags() + if err := f.Parse(args); err != nil { + c.UI.Error(err.Error()) + return 1 + } + + args = f.Args() + + if len(args) < 1 { + c.UI.Error("Not enough arguments (expected potential parent, got nothing)") + return 1 + } else if len(args) > 2 { + for _, arg := range args { + if strings.HasPrefix(arg, "-") { + c.UI.Warn(fmt.Sprintf("Options (%v) must be specified before positional arguments (%v)", arg, args[0])) + break + } + } + } + + client, err := c.Client() + if err != nil { + c.UI.Error(fmt.Sprintf("Failed to obtain client: %s", err)) + return 1 + } + + issuer := sanitizePath(args[0]) + var issued []string + if len(args) > 1 { + for _, arg := range args[1:] { + cleanPath := sanitizePath(arg) + // Arg Might be a Fully Qualified Path + if strings.Contains(cleanPath, "/issuer/") || + strings.Contains(cleanPath, "/certs/") || + strings.Contains(cleanPath, "/revoked/") { + issued = append(issued, cleanPath) + } else { // Or Arg Might be a Mount + mountCaList, err := c.getIssuerListFromMount(client, arg) + if err != nil { + c.UI.Error(err.Error()) + return 1 + } + issued = append(issued, mountCaList...) + } + } + } else { + mountListRaw, err := client.Logical().Read("/sys/mounts/") + if err != nil { + c.UI.Error(fmt.Sprintf("Failed to Read List of Mounts With Potential Issuers: %v", err)) + return 1 + } + for path, rawValueMap := range mountListRaw.Data { + valueMap := rawValueMap.(map[string]interface{}) + if valueMap["type"].(string) == "pki" { + mountCaList, err := c.getIssuerListFromMount(client, sanitizePath(path)) + if err != nil { + c.UI.Error(err.Error()) + return 1 + } + issued = append(issued, mountCaList...) + } + } + } + + childrenMatches := make(map[string]bool) + + constraintMap := map[string]bool{ + // This comparison isn't strictly correct, despite a standard ordering these are sets + "subject_match": c.flagSubjectMatch, + "path_match": c.flagPathMatch, + "trust_match": c.flagIndirectSignMatch, + "key_id_match": c.flagKeyIdMatch, + "signature_match": c.flagSignatureMatch, + } + + for _, child := range issued { + path := sanitizePath(child) + if path != "" { + err, verifyResults := verifySignBetween(client, issuer, path) + if err != nil { + c.UI.Error(fmt.Sprintf("Failed to run verification on path %v: %v", path, err)) + return 1 + } + childrenMatches[path] = checkIfResultsMatchFilters(verifyResults, constraintMap) + } + } + + err = c.outputResults(childrenMatches) + if err != nil { + c.UI.Error(err.Error()) + return 1 + } + + return 0 +} + +func (c *PKIListChildrenCommand) getIssuerListFromMount(client *api.Client, mountString string) ([]string, error) { + var issuerList []string + issuerListEndpoint := sanitizePath(mountString) + "/issuers" + rawIssuersResp, err := client.Logical().List(issuerListEndpoint) + if err != nil { + return issuerList, fmt.Errorf("failed to read list of issuers within mount %v: %v", mountString, err) + } + if rawIssuersResp == nil { // No Issuers (Empty Mount) + return issuerList, nil + } + issuersMap := rawIssuersResp.Data["keys"] + certList := issuersMap.([]interface{}) + for _, certId := range certList { + identifier := certId.(string) + if c.flagUseNames { + issuerReadResp, err := client.Logical().Read(sanitizePath(mountString) + "/issuer/" + identifier) + if err != nil { + c.UI.Warn(fmt.Sprintf("Unable to Fetch Issuer to Recover Name at: %v", sanitizePath(mountString)+"/issuer/"+identifier)) + } + if issuerReadResp != nil { + issuerName := issuerReadResp.Data["issuer_name"].(string) + if issuerName != "" { + identifier = issuerName + } + } + } + issuerList = append(issuerList, sanitizePath(mountString)+"/issuer/"+identifier) + } + return issuerList, nil +} + +func checkIfResultsMatchFilters(verifyResults, constraintMap map[string]bool) bool { + for key, required := range constraintMap { + if required == true { + if verifyResults[key] == false { + return false + } + } + } + return true +} + +func (c *PKIListChildrenCommand) outputResults(results map[string]bool) error { + switch Format(c.UI) { + case "", "table": + return c.outputResultsTable(results) + case "json": + return c.outputResultsJSON(results) + case "yaml": + return c.outputResultsYAML(results) + default: + return fmt.Errorf("unknown output format: %v", Format(c.UI)) + } +} + +func (c *PKIListChildrenCommand) outputResultsTable(results map[string]bool) error { + data := []string{"intermediate" + hopeDelim + "match?"} + for field, finding := range results { + row := field + hopeDelim + strconv.FormatBool(finding) + data = append(data, row) + } + c.UI.Output(tableOutput(data, &columnize.Config{ + Delim: hopeDelim, + })) + c.UI.Output("\n") + + return nil +} + +func (c *PKIListChildrenCommand) outputResultsJSON(results map[string]bool) error { + bytes, err := json.MarshalIndent(results, "", " ") + if err != nil { + return err + } + + c.UI.Output(string(bytes)) + return nil +} + +func (c *PKIListChildrenCommand) outputResultsYAML(results map[string]bool) error { + bytes, err := yaml.Marshal(results) + if err != nil { + return err + } + + c.UI.Output(string(bytes)) + return nil +} diff --git a/command/pki_list_children_test.go b/command/pki_list_children_test.go new file mode 100644 index 000000000..23f642fd6 --- /dev/null +++ b/command/pki_list_children_test.go @@ -0,0 +1,237 @@ +package command + +import ( + "strings" + "testing" +) + +func TestPKIListChildren(t *testing.T) { + t.Parallel() + + client, closer := testVaultServer(t) + defer closer() + + // Relationship Map to Create + // pki-root | pki-newroot | pki-empty + // RootX1 RootX2 RootX4 RootX3 + // | | + // ---------------------------------------------- + // v v + // IntX1 IntX2 pki-int + // | | + // v v + // IntX3 (-----------------------) IntX3(also) + // + // Here X1,X2 have the same name (same mount) + // RootX4 uses the same key as RootX1 (but a different common_name/subject) + // RootX3 has the same name, and is on a different mount + // RootX1 has issued IntX1; RootX3 has issued IntX2 + createComplicatedIssuerSetUp(t, client) + + cases := []struct { + name string + args []string + expectedMatches map[string]bool + jsonOut bool + shouldError bool + expectErrorCont string + expectErrorNotCont string + nonJsonOutputCont string + }{ + { + "rootX1-match-everything-no-constraints", + []string{ + "pki", "list-intermediates", "-format=json", "-use_names=true", + "-subject_match=false", "-key_id_match=false", "-direct_sign=false", "-indirect_sign=false", "-path_match=false", + "pki-root/issuer/rootX1", + }, + map[string]bool{ + "pki-root/issuer/rootX1": true, + "pki-root/issuer/rootX2": true, + "pki-newroot/issuer/rootX3": true, + "pki-root/issuer/rootX4": true, + "pki-int/issuer/intX1": true, + "pki-int/issuer/intX2": true, + "pki-int/issuer/intX3": true, + "pki-int/issuer/intX3also": true, + "pki-int/issuer/rootX1": true, + "pki-int/issuer/rootX3": true, + }, + true, + false, + "", + "", + "", + }, + { + "rootX1-default-children", + []string{"pki", "list-intermediates", "-format=json", "-use_names=true", "pki-root/issuer/rootX1"}, + map[string]bool{ + "pki-root/issuer/rootX1": true, + "pki-root/issuer/rootX2": false, + "pki-newroot/issuer/rootX3": false, + "pki-root/issuer/rootX4": false, + "pki-int/issuer/intX1": true, + "pki-int/issuer/intX2": false, + "pki-int/issuer/intX3": false, + "pki-int/issuer/intX3also": false, + "pki-int/issuer/rootX1": true, + "pki-int/issuer/rootX3": false, + }, + true, + false, + "", + "", + "", + }, + { + "rootX1-subject-match-only", + []string{ + "pki", "list-intermediates", "-format=json", "-use_names=true", + "-key_id_match=false", "-direct_sign=false", "-indirect_sign=false", + "pki-root/issuer/rootX1", + }, + map[string]bool{ + "pki-root/issuer/rootX1": true, + "pki-root/issuer/rootX2": true, + "pki-newroot/issuer/rootX3": true, + "pki-root/issuer/rootX4": false, + "pki-int/issuer/intX1": true, + "pki-int/issuer/intX2": true, + "pki-int/issuer/intX3": false, + "pki-int/issuer/intX3also": false, + "pki-int/issuer/rootX1": true, + "pki-int/issuer/rootX3": true, + }, + true, + false, + "", + "", + "", + }, + { + "rootX1-in-path", + []string{ + "pki", "list-intermediates", "-format=json", "-use_names=true", + "-subject_match=false", "-key_id_match=false", "-direct_sign=false", "-indirect_sign=false", "-path_match=true", + "pki-root/issuer/rootX1", + }, + map[string]bool{ + "pki-root/issuer/rootX1": true, + "pki-root/issuer/rootX2": false, + "pki-newroot/issuer/rootX3": false, + "pki-root/issuer/rootX4": false, + "pki-int/issuer/intX1": true, + "pki-int/issuer/intX2": false, + "pki-int/issuer/intX3": true, + "pki-int/issuer/intX3also": false, + "pki-int/issuer/rootX1": true, + "pki-int/issuer/rootX3": false, + }, + true, + false, + "", + "", + "", + }, + { + "rootX1-only-int-mount", + []string{ + "pki", "list-intermediates", "-format=json", "-use_names=true", + "-subject_match=false", "-key_id_match=false", "-direct_sign=false", "-indirect_sign=false", "-path_match=true", + "pki-root/issuer/rootX1", "pki-int/", + }, + map[string]bool{ + "pki-int/issuer/intX1": true, + "pki-int/issuer/intX2": false, + "pki-int/issuer/intX3": true, + "pki-int/issuer/intX3also": false, + "pki-int/issuer/rootX1": true, + "pki-int/issuer/rootX3": false, + }, + true, + false, + "", + "", + "", + }, + { + "rootX1-subject-match-root-mounts-only", + []string{ + "pki", "list-intermediates", "-format=json", "-use_names=true", + "-key_id_match=false", "-direct_sign=false", "-indirect_sign=false", + "pki-root/issuer/rootX1", "pki-root/", "pki-newroot", "pki-empty", + }, + map[string]bool{ + "pki-root/issuer/rootX1": true, + "pki-root/issuer/rootX2": true, + "pki-newroot/issuer/rootX3": true, + "pki-root/issuer/rootX4": false, + }, + true, + false, + "", + "", + "", + }, + { + "rootX1-subject-match-these-certs-only", + []string{ + "pki", "list-intermediates", "-format=json", "-use_names=true", + "-key_id_match=false", "-direct_sign=false", "-indirect_sign=false", + "pki-root/issuer/rootX1", "pki-root/issuer/rootX2", "pki-newroot/issuer/rootX3", "pki-root/issuer/rootX4", + }, + map[string]bool{ + "pki-root/issuer/rootX2": true, + "pki-newroot/issuer/rootX3": true, + "pki-root/issuer/rootX4": false, + }, + true, + false, + "", + "", + "", + }, + } + for _, testCase := range cases { + var errString string + var results map[string]interface{} + var stdOut string + + if testCase.jsonOut { + results, errString = execPKIVerifyJson(t, client, false, testCase.shouldError, testCase.args) + } else { + stdOut, errString = execPKIVerifyNonJson(t, client, testCase.shouldError, testCase.args) + } + + // Verify Error Behavior + if testCase.shouldError { + if errString == "" { + t.Fatalf("Expected error in Testcase %s : no error produced, got results %s", testCase.name, results) + } + if testCase.expectErrorCont != "" && !strings.Contains(errString, testCase.expectErrorCont) { + t.Fatalf("Expected error in Testcase %s to contain %s, but got error %s", testCase.name, testCase.expectErrorCont, errString) + } + if testCase.expectErrorNotCont != "" && strings.Contains(errString, testCase.expectErrorNotCont) { + t.Fatalf("Expected error in Testcase %s to not contain %s, but got error %s", testCase.name, testCase.expectErrorNotCont, errString) + } + } else { + if errString != "" { + t.Fatalf("Error in Testcase %s : no error expected, but got error: %s", testCase.name, errString) + } + } + + // Verify Output + if testCase.jsonOut { + isMatch, errString := verifyExpectedJson(testCase.expectedMatches, results) + if !isMatch { + t.Fatalf("Expected Results for Testcase %s, do not match returned results %s", testCase.name, errString) + } + } else { + if !strings.Contains(stdOut, testCase.nonJsonOutputCont) { + t.Fatalf("Expected standard output for Testcase %s to contain %s, but got %s", testCase.name, testCase.nonJsonOutputCont, stdOut) + } + } + + } +} diff --git a/command/pki_verify_sign_command.go b/command/pki_verify_sign_command.go index ff2976cd1..ce00eeee5 100644 --- a/command/pki_verify_sign_command.go +++ b/command/pki_verify_sign_command.go @@ -115,7 +115,7 @@ func verifySignBetween(client *api.Client, issuerPath string, issuedPath string) return fmt.Errorf("error: unable to fetch issuer %v: %w", issuerPath, err), nil } if len(issuedPath) <= 2 { - return fmt.Errorf(fmt.Sprintf("%v", issuedPath)), nil + return fmt.Errorf("%v", issuedPath), nil } caChainRaw := issuedCertResp.Data["ca_chain"] if caChainRaw == nil { diff --git a/command/pki_verify_sign_test.go b/command/pki_verify_sign_test.go index 0a3b239bb..12e5698fa 100644 --- a/command/pki_verify_sign_test.go +++ b/command/pki_verify_sign_test.go @@ -2,6 +2,7 @@ package command import ( "bytes" + "context" "encoding/json" "fmt" "strings" @@ -159,7 +160,7 @@ func convertListOfInterfaceToString(list []interface{}, sep string) string { func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) { // Relationship Map to Create - // pki-root | pki-newroot + // pki-root | pki-newroot | pki-empty // RootX1 RootX2 RootX4 RootX3 // | | // ---------------------------------------------- @@ -201,6 +202,14 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) { t.Fatalf("pki mount error: %#v", err) } + // Used to check handling empty list responses: Not Used for Any Issuers / Certificates + if err := client.Sys().Mount("pki-empty", &api.MountInput{ + Type: "pki", + Config: api.MountConfigInput{}, + }); err != nil { + t.Fatalf("pki mount error: %#v", err) + } + resp, err := client.Logical().Write("pki-root/root/generate/internal", map[string]interface{}{ "key_type": "ec", "common_name": "Root X", @@ -208,7 +217,7 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) { "issuer_name": "rootX1", "key_name": "rootX1", }) - t.Logf("%s", resp.Data) + if err != nil || resp == nil { t.Fatalf("failed to prime CA: %v", err) } @@ -219,7 +228,7 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) { "ttl": "3650d", "issuer_name": "rootX2", }) - t.Logf("%s", resp.Data) + if err != nil || resp == nil { t.Fatalf("failed to prime CA: %v", err) } @@ -251,6 +260,10 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) { if err != nil || int1CsrResp == nil { t.Fatalf("failed to generate CSR: %v", err) } + int1KeyId, ok := int1CsrResp.Data["key_id"] + if !ok { + t.Fatalf("no key_id produced when generating csr, response %v", int1CsrResp.Data) + } int1CsrRaw, ok := int1CsrResp.Data["csr"] if !ok { t.Fatalf("no csr produced when generating intermediate, resp: %v", int1CsrResp) @@ -277,18 +290,24 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) { if !ok { t.Fatalf("no mapping data returned on issuer import: %v", importInt1Resp) } - importIssuerId := "" for key, value := range importIssuerIdMap.(map[string]interface{}) { if value != nil && len(value.(string)) > 0 { - importIssuerId = key - break + if value != int1KeyId { + t.Fatalf("Expected exactly one key_match to %v, got multiple: %v", int1KeyId, importIssuerIdMap) + } + if resp, err := client.Logical().JSONMergePatch(context.Background(), "pki-int/issuer/"+key, map[string]interface{}{ + "issuer_name": "intX1", + }); err != nil || resp == nil { + t.Fatalf("error naming issuer %v", err) + } + } else { + if resp, err := client.Logical().JSONMergePatch(context.Background(), "pki-int/issuer/"+key, map[string]interface{}{ + "issuer_name": "rootX1", + }); err != nil || resp == nil { + t.Fatalf("error naming issuer parent %v", err) + } } } - if resp, err := client.Logical().Write("pki-int/issuer/"+importIssuerId, map[string]interface{}{ - "issuer_name": "intX1", - }); err != nil || resp == nil { - t.Fatalf("error naming issuer %v", err) - } // Intermediate X2 int2CsrResp, err := client.Logical().Write("pki-int/intermediate/generate/internal", map[string]interface{}{ @@ -299,6 +318,10 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) { if err != nil || int2CsrResp == nil { t.Fatalf("failed to generate CSR: %v", err) } + int2KeyId, ok := int2CsrResp.Data["key_id"] + if !ok { + t.Fatalf("no key material returned from producing csr, resp: %v", int2CsrResp) + } int2CsrRaw, ok := int2CsrResp.Data["csr"] if !ok { t.Fatalf("no csr produced when generating intermediate, resp: %v", int2CsrResp) @@ -316,8 +339,7 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) { } int2CertChain := convertListOfInterfaceToString(int2CertChainRaw.([]interface{}), "\n") importInt2Resp, err := client.Logical().Write("pki-int/issuers/import/cert", map[string]interface{}{ - "pem_bundle": int2CertChain, - "issuer_name": "intX2", + "pem_bundle": int2CertChain, }) if err != nil || importInt2Resp == nil { t.Fatalf("failed to import certificate: %v", err) @@ -326,18 +348,24 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) { if !ok { t.Fatalf("no mapping data returned on issuer import: %v", importInt2Resp) } - importIssuer2Id := "" for key, value := range importIssuer2IdMap.(map[string]interface{}) { if value != nil && len(value.(string)) > 0 { - importIssuer2Id = key - break + if value != int2KeyId { + t.Fatalf("unexpected key_match with ca_chain, expected only %v, got %v", int2KeyId, importIssuer2IdMap) + } + if resp, err := client.Logical().JSONMergePatch(context.Background(), "pki-int/issuer/"+key, map[string]interface{}{ + "issuer_name": "intX2", + }); err != nil || resp == nil { + t.Fatalf("error naming issuer %v", err) + } + } else { + if resp, err := client.Logical().Write("pki-int/issuer/"+key, map[string]interface{}{ + "issuer_name": "rootX3", + }); err != nil || resp == nil { + t.Fatalf("error naming parent issuer %v", err) + } } } - if resp, err := client.Logical().Write("pki-int/issuer/"+importIssuer2Id, map[string]interface{}{ - "issuer_name": "intX2", - }); err != nil || resp == nil { - t.Fatalf("error naming issuer %v", err) - } // Intermediate X3 int3CsrResp, err := client.Logical().Write("pki-int/intermediate/generate/internal", map[string]interface{}{ @@ -348,6 +376,7 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) { if err != nil || int3CsrResp == nil { t.Fatalf("failed to generate CSR: %v", err) } + int3KeyId, ok := int3CsrResp.Data["key_id"] int3CsrRaw, ok := int3CsrResp.Data["csr"] if !ok { t.Fatalf("no csr produced when generating intermediate, resp: %v", int3CsrResp) @@ -375,18 +404,16 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) { if !ok { t.Fatalf("no mapping data returned on issuer import: %v", importInt2Resp) } - importIssuer3Id1 := "" for key, value := range importIssuer3IdMap1.(map[string]interface{}) { - if value != nil && len(value.(string)) > 0 { - importIssuer3Id1 = key + if value != nil && len(value.(string)) > 0 && value == int3KeyId { + if resp, err := client.Logical().JSONMergePatch(context.Background(), "pki-int/issuer/"+key, map[string]interface{}{ + "issuer_name": "intX3", + }); err != nil || resp == nil { + t.Fatalf("error naming issuer %v", err) + } break } } - if resp, err := client.Logical().Write("pki-int/issuer/"+importIssuer3Id1, map[string]interface{}{ - "issuer_name": "intX3", - }); err != nil || resp == nil { - t.Fatalf("error naming issuer %v", err) - } // sign by intX2 and import int3CertResp2, err := client.Logical().Write("pki-int/issuer/intX2/sign-intermediate", map[string]interface{}{ "csr": int3Csr, @@ -409,18 +436,16 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) { if !ok { t.Fatalf("no mapping data returned on issuer import: %v", importInt2Resp) } - importIssuer3Id2 := "" for key, value := range importIssuer3IdMap2.(map[string]interface{}) { - if value != nil && len(value.(string)) > 0 { - importIssuer3Id2 = key - break + if value != nil && len(value.(string)) > 0 && value == int3KeyId { + if resp, err := client.Logical().JSONMergePatch(context.Background(), "pki-int/issuer/"+key, map[string]interface{}{ + "issuer_name": "intX3also", + }); err != nil || resp == nil { + t.Fatalf("error naming issuer %v", err) + } + break // Parent Certs Already Named } } - if resp, err := client.Logical().Write("pki-int/issuer/"+importIssuer3Id2, map[string]interface{}{ - "issuer_name": "intX3also", - }); err != nil || resp == nil { - t.Fatalf("error naming issuer %v", err) - } } func verifyExpectedJson(expectedResults map[string]bool, results map[string]interface{}) (isMatch bool, error string) {