Vault 11796 vault cli list intermediates (#18463)

* Base functionality.

* make fmt; changelog

* What I thought empty issuers response fix would be.

* Fix null response data for listing empty issuers causing a crash.

* Update command/pki_list_children_command.go

Fix double specifier

Co-authored-by: Steven Clark <steven.clark@hashicorp.com>

* Add test for pki_list_children.

* Fix tests.

* Update descriptions for correctness based on PR reviews.

Co-authored-by: Steven Clark <steven.clark@hashicorp.com>
This commit is contained in:
Kit Haines 2023-01-27 10:34:31 -05:00 committed by GitHub
parent 4a9610f382
commit 1cef81f025
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 597 additions and 39 deletions

3
changelog/18463.txt Normal file
View file

@ -0,0 +1,3 @@
```release-note:improvement
cli/pki: Add List-Intermediates functionality to pki client.
```

View file

@ -543,6 +543,11 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
BaseCommand: getBaseCommand(), BaseCommand: getBaseCommand(),
}, nil }, nil
}, },
"pki list-intermediates": func() (cli.Command, error) {
return &PKIListChildrenCommand{
BaseCommand: getBaseCommand(),
}, nil
},
"pki verify-sign": func() (cli.Command, error) { "pki verify-sign": func() (cli.Command, error) {
return &PKIVerifySignCommand{ return &PKIVerifySignCommand{
BaseCommand: getBaseCommand(), BaseCommand: getBaseCommand(),

View file

@ -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
}

View file

@ -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)
}
}
}
}

View file

@ -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 return fmt.Errorf("error: unable to fetch issuer %v: %w", issuerPath, err), nil
} }
if len(issuedPath) <= 2 { if len(issuedPath) <= 2 {
return fmt.Errorf(fmt.Sprintf("%v", issuedPath)), nil return fmt.Errorf("%v", issuedPath), nil
} }
caChainRaw := issuedCertResp.Data["ca_chain"] caChainRaw := issuedCertResp.Data["ca_chain"]
if caChainRaw == nil { if caChainRaw == nil {

View file

@ -2,6 +2,7 @@ package command
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings" "strings"
@ -159,7 +160,7 @@ func convertListOfInterfaceToString(list []interface{}, sep string) string {
func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) { func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) {
// Relationship Map to Create // Relationship Map to Create
// pki-root | pki-newroot // pki-root | pki-newroot | pki-empty
// RootX1 RootX2 RootX4 RootX3 // RootX1 RootX2 RootX4 RootX3
// | | // | |
// ---------------------------------------------- // ----------------------------------------------
@ -201,6 +202,14 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) {
t.Fatalf("pki mount error: %#v", err) 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{}{ resp, err := client.Logical().Write("pki-root/root/generate/internal", map[string]interface{}{
"key_type": "ec", "key_type": "ec",
"common_name": "Root X", "common_name": "Root X",
@ -208,7 +217,7 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) {
"issuer_name": "rootX1", "issuer_name": "rootX1",
"key_name": "rootX1", "key_name": "rootX1",
}) })
t.Logf("%s", resp.Data)
if err != nil || resp == nil { if err != nil || resp == nil {
t.Fatalf("failed to prime CA: %v", err) t.Fatalf("failed to prime CA: %v", err)
} }
@ -219,7 +228,7 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) {
"ttl": "3650d", "ttl": "3650d",
"issuer_name": "rootX2", "issuer_name": "rootX2",
}) })
t.Logf("%s", resp.Data)
if err != nil || resp == nil { if err != nil || resp == nil {
t.Fatalf("failed to prime CA: %v", err) 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 { if err != nil || int1CsrResp == nil {
t.Fatalf("failed to generate CSR: %v", err) 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"] int1CsrRaw, ok := int1CsrResp.Data["csr"]
if !ok { if !ok {
t.Fatalf("no csr produced when generating intermediate, resp: %v", int1CsrResp) 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 { if !ok {
t.Fatalf("no mapping data returned on issuer import: %v", importInt1Resp) t.Fatalf("no mapping data returned on issuer import: %v", importInt1Resp)
} }
importIssuerId := ""
for key, value := range importIssuerIdMap.(map[string]interface{}) { for key, value := range importIssuerIdMap.(map[string]interface{}) {
if value != nil && len(value.(string)) > 0 { if value != nil && len(value.(string)) > 0 {
importIssuerId = key if value != int1KeyId {
break 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{}{
if resp, err := client.Logical().Write("pki-int/issuer/"+importIssuerId, map[string]interface{}{
"issuer_name": "intX1", "issuer_name": "intX1",
}); err != nil || resp == nil { }); err != nil || resp == nil {
t.Fatalf("error naming issuer %v", err) 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)
}
}
}
// Intermediate X2 // Intermediate X2
int2CsrResp, err := client.Logical().Write("pki-int/intermediate/generate/internal", map[string]interface{}{ 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 { if err != nil || int2CsrResp == nil {
t.Fatalf("failed to generate CSR: %v", err) 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"] int2CsrRaw, ok := int2CsrResp.Data["csr"]
if !ok { if !ok {
t.Fatalf("no csr produced when generating intermediate, resp: %v", int2CsrResp) t.Fatalf("no csr produced when generating intermediate, resp: %v", int2CsrResp)
@ -317,7 +340,6 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) {
int2CertChain := convertListOfInterfaceToString(int2CertChainRaw.([]interface{}), "\n") int2CertChain := convertListOfInterfaceToString(int2CertChainRaw.([]interface{}), "\n")
importInt2Resp, err := client.Logical().Write("pki-int/issuers/import/cert", map[string]interface{}{ importInt2Resp, err := client.Logical().Write("pki-int/issuers/import/cert", map[string]interface{}{
"pem_bundle": int2CertChain, "pem_bundle": int2CertChain,
"issuer_name": "intX2",
}) })
if err != nil || importInt2Resp == nil { if err != nil || importInt2Resp == nil {
t.Fatalf("failed to import certificate: %v", err) t.Fatalf("failed to import certificate: %v", err)
@ -326,18 +348,24 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) {
if !ok { if !ok {
t.Fatalf("no mapping data returned on issuer import: %v", importInt2Resp) t.Fatalf("no mapping data returned on issuer import: %v", importInt2Resp)
} }
importIssuer2Id := ""
for key, value := range importIssuer2IdMap.(map[string]interface{}) { for key, value := range importIssuer2IdMap.(map[string]interface{}) {
if value != nil && len(value.(string)) > 0 { if value != nil && len(value.(string)) > 0 {
importIssuer2Id = key if value != int2KeyId {
break 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{}{
if resp, err := client.Logical().Write("pki-int/issuer/"+importIssuer2Id, map[string]interface{}{
"issuer_name": "intX2", "issuer_name": "intX2",
}); err != nil || resp == nil { }); err != nil || resp == nil {
t.Fatalf("error naming issuer %v", err) 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)
}
}
}
// Intermediate X3 // Intermediate X3
int3CsrResp, err := client.Logical().Write("pki-int/intermediate/generate/internal", map[string]interface{}{ 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 { if err != nil || int3CsrResp == nil {
t.Fatalf("failed to generate CSR: %v", err) t.Fatalf("failed to generate CSR: %v", err)
} }
int3KeyId, ok := int3CsrResp.Data["key_id"]
int3CsrRaw, ok := int3CsrResp.Data["csr"] int3CsrRaw, ok := int3CsrResp.Data["csr"]
if !ok { if !ok {
t.Fatalf("no csr produced when generating intermediate, resp: %v", int3CsrResp) 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 { if !ok {
t.Fatalf("no mapping data returned on issuer import: %v", importInt2Resp) t.Fatalf("no mapping data returned on issuer import: %v", importInt2Resp)
} }
importIssuer3Id1 := ""
for key, value := range importIssuer3IdMap1.(map[string]interface{}) { for key, value := range importIssuer3IdMap1.(map[string]interface{}) {
if value != nil && len(value.(string)) > 0 { if value != nil && len(value.(string)) > 0 && value == int3KeyId {
importIssuer3Id1 = key if resp, err := client.Logical().JSONMergePatch(context.Background(), "pki-int/issuer/"+key, map[string]interface{}{
break
}
}
if resp, err := client.Logical().Write("pki-int/issuer/"+importIssuer3Id1, map[string]interface{}{
"issuer_name": "intX3", "issuer_name": "intX3",
}); err != nil || resp == nil { }); err != nil || resp == nil {
t.Fatalf("error naming issuer %v", err) t.Fatalf("error naming issuer %v", err)
} }
break
}
}
// sign by intX2 and import // sign by intX2 and import
int3CertResp2, err := client.Logical().Write("pki-int/issuer/intX2/sign-intermediate", map[string]interface{}{ int3CertResp2, err := client.Logical().Write("pki-int/issuer/intX2/sign-intermediate", map[string]interface{}{
"csr": int3Csr, "csr": int3Csr,
@ -409,18 +436,16 @@ func createComplicatedIssuerSetUp(t *testing.T, client *api.Client) {
if !ok { if !ok {
t.Fatalf("no mapping data returned on issuer import: %v", importInt2Resp) t.Fatalf("no mapping data returned on issuer import: %v", importInt2Resp)
} }
importIssuer3Id2 := ""
for key, value := range importIssuer3IdMap2.(map[string]interface{}) { for key, value := range importIssuer3IdMap2.(map[string]interface{}) {
if value != nil && len(value.(string)) > 0 { if value != nil && len(value.(string)) > 0 && value == int3KeyId {
importIssuer3Id2 = key if resp, err := client.Logical().JSONMergePatch(context.Background(), "pki-int/issuer/"+key, map[string]interface{}{
break
}
}
if resp, err := client.Logical().Write("pki-int/issuer/"+importIssuer3Id2, map[string]interface{}{
"issuer_name": "intX3also", "issuer_name": "intX3also",
}); err != nil || resp == nil { }); err != nil || resp == nil {
t.Fatalf("error naming issuer %v", err) t.Fatalf("error naming issuer %v", err)
} }
break // Parent Certs Already Named
}
}
} }
func verifyExpectedJson(expectedResults map[string]bool, results map[string]interface{}) (isMatch bool, error string) { func verifyExpectedJson(expectedResults map[string]bool, results map[string]interface{}) (isMatch bool, error string) {