diff --git a/builtin/logical/pki/backend.go b/builtin/logical/pki/backend.go index 210c710c2b..bf514df38e 100644 --- a/builtin/logical/pki/backend.go +++ b/builtin/logical/pki/backend.go @@ -48,6 +48,7 @@ func Backend() *framework.Backend { pathFetchCRL(&b), pathFetchCRLViaCertPath(&b), pathFetchValid(&b), + pathFetchListCerts(&b), pathRevoke(&b), pathTidy(&b), }, diff --git a/builtin/logical/pki/backend_test.go b/builtin/logical/pki/backend_test.go index f1efe6d3f3..29ab6fe8e0 100644 --- a/builtin/logical/pki/backend_test.go +++ b/builtin/logical/pki/backend_test.go @@ -1784,6 +1784,133 @@ func generateRoleSteps(t *testing.T, useCSRs bool) []logicaltest.TestStep { return ret } +func TestBackend_PathFetchCertList(t *testing.T) { + // create the backend + config := logical.TestBackendConfig() + storage := &logical.InmemStorage{} + config.StorageView = storage + + b := Backend() + _, err := b.Setup(config) + if err != nil { + t.Fatal(err) + } + + // generate root + rootData := map[string]interface{}{ + "common_name": "test.com", + "ttl": "6h", + } + + resp, err := b.HandleRequest(&logical.Request{ + Operation: logical.UpdateOperation, + Path: "root/generate/internal", + Storage: storage, + Data: rootData, + }) + if resp != nil && resp.IsError() { + t.Fatalf("failed to generate root, %#v", resp) + } + if err != nil { + t.Fatal(err) + } + + // config urls + urlsData := map[string]interface{}{ + "issuing_certificates": "http://127.0.0.1:8200/v1/pki/ca", + "crl_distribution_points": "http://127.0.0.1:8200/v1/pki/crl", + } + + resp, err = b.HandleRequest(&logical.Request{ + Operation: logical.UpdateOperation, + Path: "config/urls", + Storage: storage, + Data: urlsData, + }) + if resp != nil && resp.IsError() { + t.Fatalf("failed to config urls, %#v", resp) + } + if err != nil { + t.Fatal(err) + } + + // create a role entry + roleData := map[string]interface{}{ + "allowed_domains": "test.com", + "allow_subdomains": "true", + "max_ttl": "4h", + } + + resp, err = b.HandleRequest(&logical.Request{ + Operation: logical.UpdateOperation, + Path: "roles/test-example", + Storage: storage, + Data: roleData, + }) + if resp != nil && resp.IsError() { + t.Fatalf("failed to create a role, %#v", resp) + } + if err != nil { + t.Fatal(err) + } + + // issue some certs + i := 1 + for i < 10 { + certData := map[string]interface{}{ + "common_name": "example.test.com", + } + resp, err = b.HandleRequest(&logical.Request{ + Operation: logical.UpdateOperation, + Path: "issue/test-example", + Storage: storage, + Data: certData, + }) + if resp != nil && resp.IsError() { + t.Fatalf("failed to issue a cert, %#v", resp) + } + if err != nil { + t.Fatal(err) + } + + i = i + 1 + } + + // list certs + resp, err = b.HandleRequest(&logical.Request{ + Operation: logical.ListOperation, + Path: "certs", + Storage: storage, + }) + if resp != nil && resp.IsError() { + t.Fatalf("failed to list certs, %#v", resp) + } + if err != nil { + t.Fatal(err) + } + // check that the root and 9 additional certs are all listed + if len(resp.Data["keys"].([]string)) != 10 { + t.Fatalf("failed to list all 10 certs") + } + + // list certs/ + resp, err = b.HandleRequest(&logical.Request{ + Operation: logical.ListOperation, + Path: "certs/", + Storage: storage, + }) + if resp != nil && resp.IsError() { + t.Fatalf("failed to list certs, %#v", resp) + } + if err != nil { + t.Fatal(err) + } + // check that the root and 9 additional certs are all listed + if len(resp.Data["keys"].([]string)) != 10 { + t.Fatalf("failed to list all 10 certs") + } +} + const ( rsaCAKey string = `-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA1eKB2nFbRqTFs7KyZjbzB5VRCBbnLZfEXVP1c3bHe+YGjlfl diff --git a/builtin/logical/pki/path_fetch.go b/builtin/logical/pki/path_fetch.go index ed63682e2b..b37e9ff0fe 100644 --- a/builtin/logical/pki/path_fetch.go +++ b/builtin/logical/pki/path_fetch.go @@ -73,6 +73,29 @@ func pathFetchCRLViaCertPath(b *backend) *framework.Path { } } +// This returns the list of serial numbers for certs +func pathFetchListCerts(b *backend) *framework.Path { + return &framework.Path{ + Pattern: "certs/?$", + + Callbacks: map[logical.Operation]framework.OperationFunc{ + logical.ListOperation: b.pathFetchCertList, + }, + + HelpSynopsis: pathFetchHelpSyn, + HelpDescription: pathFetchHelpDesc, + } +} + +func (b *backend) pathFetchCertList(req *logical.Request, data *framework.FieldData) (response *logical.Response, retErr error) { + entries, err := req.Storage.List("certs/") + if err != nil { + return nil, err + } + + return logical.ListResponse(entries), nil +} + func (b *backend) pathFetchRead(req *logical.Request, data *framework.FieldData) (response *logical.Response, retErr error) { var serial, pemType, contentType string var certEntry, revokedEntry *logical.StorageEntry diff --git a/website/source/docs/secrets/pki/index.html.md b/website/source/docs/secrets/pki/index.html.md index f8da8092c3..f0f9d13d8c 100644 --- a/website/source/docs/secrets/pki/index.html.md +++ b/website/source/docs/secrets/pki/index.html.md @@ -423,6 +423,50 @@ subpath for interactive help output. +### /pki/certs/ +#### LIST + +