PKI Responses Part 4 (#18612)

This commit is contained in:
AnPucel 2023-03-14 15:00:37 -07:00 committed by GitHub
parent 85f845c3e0
commit 65e5730c6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1002 additions and 26 deletions

View File

@ -2694,6 +2694,7 @@ func TestBackend_SignSelfIssued(t *testing.T) {
},
MountPoint: "pki/",
})
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("root/sign-self-issued"), logical.UpdateOperation), resp, true)
if err != nil {
t.Fatal(err)
}
@ -5246,7 +5247,8 @@ func TestBackend_IfModifiedSinceHeaders(t *testing.T) {
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
HandlerFunc: vaulthttp.Handler,
RequestResponseCallback: schema.ResponseValidatingCallback(t),
})
cluster.Start()
defer cluster.Cleanup()
@ -5966,6 +5968,7 @@ func TestPKI_ListRevokedCerts(t *testing.T) {
// Test empty cluster
resp, err := CBList(b, s, "certs/revoked")
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("certs/revoked"), logical.ListOperation), resp, true)
requireSuccessNonNilResponse(t, resp, err, "failed listing empty cluster")
require.Empty(t, resp.Data, "response map contained data that we did not expect")

View File

@ -600,10 +600,11 @@ func TestPoP(t *testing.T) {
require.NotNil(t, resp)
require.NotEmpty(t, resp.Data["certificate"])
_, err = CBWrite(b, s, "revoke-with-key", map[string]interface{}{
resp, err = CBWrite(b, s, "revoke-with-key", map[string]interface{}{
"certificate": resp.Data["certificate"],
"private_key": resp.Data["private_key"],
})
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("revoke-with-key"), logical.UpdateOperation), resp, true)
require.NoError(t, err)
// Issue a second leaf, but hold onto it for now.
@ -780,7 +781,9 @@ func TestIssuerRevocation(t *testing.T) {
require.NotZero(t, resp.Data["revocation_time"])
// Regenerate the CRLs
_, err = CBRead(b, s, "crl/rotate")
resp, err = CBRead(b, s, "crl/rotate")
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("crl/rotate"), logical.ReadOperation), resp, true)
require.NoError(t, err)
// Ensure the old cert isn't on its own CRL.

View File

@ -25,12 +25,12 @@ var pathFetchReadSchema = map[int][]framework.Response{
Required: false,
},
"revocation_time": {
Type: framework.TypeInt,
Type: framework.TypeString,
Description: `Revocation time`,
Required: false,
},
"revocation_time_rfc3339": {
Type: framework.TypeInt,
Type: framework.TypeString,
Description: `Revocation time RFC 3339 formatted`,
Required: false,
},

View File

@ -171,67 +171,75 @@ to be set on all PR secondary clusters.`,
"issuer_id": {
Type: framework.TypeString,
Description: `Issuer Id`,
Required: true,
Required: false,
},
"issuer_name": {
Type: framework.TypeString,
Description: `Issuer Name`,
Required: true,
Required: false,
},
"key_id": {
Type: framework.TypeString,
Description: `Key Id`,
Required: true,
Required: false,
},
"certificate": {
Type: framework.TypeString,
Description: `Certificate`,
Required: true,
Required: false,
},
"manual_chain": {
Type: framework.TypeStringSlice,
Description: `Manual Chain`,
Required: true,
Required: false,
},
"ca_chain": {
Type: framework.TypeStringSlice,
Description: `CA Chain`,
Required: true,
Required: false,
},
"leaf_not_after_behavior": {
Type: framework.TypeString,
Description: `Leaf Not After Behavior`,
Required: true,
Required: false,
},
"usage": {
Type: framework.TypeStringSlice,
Description: `Usage`,
Required: true,
Required: false,
},
"revocation_signature_algorithm": {
Type: framework.TypeString,
Description: `Revocation Signature Alogrithm`,
Required: true,
Required: false,
},
"revoked": {
Type: framework.TypeBool,
Description: `Revoked`,
Required: true,
Required: false,
},
"revocation_time": {
Type: framework.TypeInt,
Required: false,
},
"revocation_time_rfc3339": {
Type: framework.TypeString,
Required: false,
},
"issuing_certificates": {
Type: framework.TypeStringSlice,
Description: `Issuing Certificates`,
Required: true,
Required: false,
},
"crl_distribution_points": {
Type: framework.TypeStringSlice,
Description: `CRL Distribution Points`,
Required: true,
Required: false,
},
"ocsp_servers": {
Type: framework.TypeStringSlice,
Description: `OSCP Servers`,
Required: true,
Required: false,
},
},
}},
@ -1067,9 +1075,8 @@ func buildPathGetIssuerCRL(b *backend, pattern string) *framework.Path {
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"crl": {
Type: framework.TypeString,
Description: ``,
Required: false,
Type: framework.TypeString,
Required: false,
},
},
}},

View File

@ -14,6 +14,8 @@ import (
"testing"
"time"
"github.com/hashicorp/vault/sdk/helper/testhelpers/schema"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/vault"
@ -197,6 +199,7 @@ func TestOcsp_UnknownIssuerIdWithDefaultHavingOcspUsageRemoved(t *testing.T) {
resp, err := CBWrite(b, s, "revoke", map[string]interface{}{
"serial_number": serial,
})
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("revoke"), logical.UpdateOperation), resp, true)
requireSuccessNonNilResponse(t, resp, err, "revoke")
// Twiddle the entry so that the issuer id is no longer valid.

View File

@ -12,6 +12,7 @@ import (
"errors"
"fmt"
"math/big"
"net/http"
"strconv"
"strings"
"time"
@ -77,6 +78,18 @@ base64 encoded. Defaults to "pem".`,
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathUpdateResignCrlsHandler,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"crl": {
Type: framework.TypeString,
Description: `CRL`,
Required: true,
},
},
}},
},
},
},
@ -133,6 +146,18 @@ value (string)`,
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathUpdateSignRevocationListHandler,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"crl": {
Type: framework.TypeString,
Description: `CRL`,
Required: true,
},
},
}},
},
},
},

View File

@ -10,6 +10,8 @@ import (
"testing"
"time"
"github.com/hashicorp/vault/sdk/helper/testhelpers/schema"
"github.com/hashicorp/vault/api"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/vault"
@ -55,6 +57,7 @@ func TestResignCrls_NormalCrl(t *testing.T) {
"format": "pem",
"crls": []string{crl1, crl2},
})
schema.ValidateResponse(t, schema.GetResponseSchema(t, b1.Route("issuer/default/resign-crls"), logical.UpdateOperation), resp, true)
requireSuccessNonNilResponse(t, resp, err)
requireFieldsSetInResp(t, resp, "crl")
pemCrl := resp.Data["crl"].(string)
@ -351,6 +354,7 @@ func TestSignRevocationList_NoRevokedCerts(t *testing.T) {
"next_update": "12h",
"format": "pem",
})
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("issuer/default/sign-revocation-list"), logical.UpdateOperation), resp, true)
requireSuccessNonNilResponse(t, resp, err)
requireFieldsSetInResp(t, resp, "crl")
pemCrl := resp.Data["crl"].(string)

View File

@ -9,6 +9,7 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"net/http"
"strings"
"time"
@ -25,6 +26,18 @@ func pathListCertsRevoked(b *backend) *framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.ListOperation: &framework.PathOperation{
Callback: b.pathListRevokedCertsHandler,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"keys": {
Type: framework.TypeStringSlice,
Description: `List of Keys`,
Required: false,
},
},
}},
},
},
},
@ -71,6 +84,28 @@ signed by an issuer in this mount.`,
// If this needs to write, the entire request will be forwarded to the
// active node of the current performance cluster, but we don't want to
// forward invalid revoke requests there.
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"revocation_time": {
Type: framework.TypeDurationSecond,
Description: `Revocation Time`,
Required: false,
},
"revocation_time_rfc3339": {
Type: framework.TypeTime,
Description: `Revocation Time`,
Required: false,
},
"state": {
Type: framework.TypeString,
Description: `Revocation State`,
Required: false,
},
},
}},
},
},
},
@ -107,6 +142,28 @@ be in PEM format.`,
// If this needs to write, the entire request will be forwarded to the
// active node of the current performance cluster, but we don't want to
// forward invalid revoke requests there.
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"revocation_time": {
Type: framework.TypeDurationSecond,
Description: `Revocation Time`,
Required: false,
},
"revocation_time_rfc3339": {
Type: framework.TypeTime,
Description: `Revocation Time`,
Required: false,
},
"state": {
Type: framework.TypeString,
Description: `Revocation State`,
Required: false,
},
},
}},
},
},
},
@ -126,6 +183,18 @@ func pathRotateCRL(b *backend) *framework.Path {
// so this request should be forwarded when it is first seen, not
// when it is ready to write.
ForwardPerformanceStandby: true,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"success": {
Type: framework.TypeBool,
Description: `Whether rotation was successful`,
Required: true,
},
},
}},
},
},
},
@ -145,6 +214,18 @@ func pathRotateDeltaCRL(b *backend) *framework.Path {
// so this request should be forwarded when it is first seen, not
// when it is ready to write.
ForwardPerformanceStandby: true,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"success": {
Type: framework.TypeBool,
Description: `Whether rotation was successful`,
Required: true,
},
},
}},
},
},
},
@ -160,6 +241,23 @@ func pathListUnifiedRevoked(b *backend) *framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.ListOperation: &framework.PathOperation{
Callback: b.pathListUnifiedRevokedCertsHandler,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"keys": {
Type: framework.TypeStringSlice,
Description: `List of Keys`,
Required: false,
},
"key_info": {
Type: framework.TypeString,
Description: `Key information`,
Required: false,
},
},
}},
},
},
},

View File

@ -5,6 +5,7 @@ import (
"crypto/x509"
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
@ -23,6 +24,18 @@ func pathListRoles(b *backend) *framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.ListOperation: &framework.PathOperation{
Callback: b.pathRoleList,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"keys": {
Type: framework.TypeMap,
Description: `List of keys`,
Required: false,
},
},
}},
},
},
},
@ -32,6 +45,352 @@ func pathListRoles(b *backend) *framework.Path {
}
func pathRoles(b *backend) *framework.Path {
pathRolesResponse := map[string]*framework.FieldSchema{
"ttl": {
Type: framework.TypeDurationSecond,
Required: true,
Description: `The lease duration (validity period of the
certificate) if no specific lease duration is requested.
The lease duration controls the expiration of certificates
issued by this backend. Defaults to the system default
value or the value of max_ttl, whichever is shorter.`,
},
"max_ttl": {
Type: framework.TypeDurationSecond,
Required: true,
Description: `The maximum allowed lease duration. If not
set, defaults to the system maximum lease TTL.`,
},
"allow_token_displayname": {
Type: framework.TypeBool,
Required: true,
Description: `Whether to allow "localhost" and "localdomain"
as a valid common name in a request, independent of allowed_domains value.`,
},
"allow_localhost": {
Type: framework.TypeBool,
Required: true,
Description: `Whether to allow "localhost" and "localdomain"
as a valid common name in a request, independent of allowed_domains value.`,
},
"allowed_domains": {
Type: framework.TypeCommaStringSlice,
Required: true,
Description: `Specifies the domains this role is allowed
to issue certificates for. This is used with the allow_bare_domains,
allow_subdomains, and allow_glob_domains to determine matches for the
common name, DNS-typed SAN entries, and Email-typed SAN entries of
certificates. See the documentation for more information. This parameter
accepts a comma-separated string or list of domains.`,
},
"allowed_domains_template": {
Type: framework.TypeBool,
Required: true,
Description: `If set, Allowed domains can be specified using identity template policies.
Non-templated domains are also permitted.`,
},
"allow_bare_domains": {
Type: framework.TypeBool,
Required: true,
Description: `If set, clients can request certificates
for the base domains themselves, e.g. "example.com" of domains listed
in allowed_domains. This is a separate option as in some cases this can
be considered a security threat. See the documentation for more
information.`,
},
"allow_subdomains": {
Type: framework.TypeBool,
Required: true,
Description: `If set, clients can request certificates for
subdomains of domains listed in allowed_domains, including wildcard
subdomains. See the documentation for more information.`,
},
"allow_glob_domains": {
Type: framework.TypeBool,
Required: true,
Description: `If set, domains specified in allowed_domains
can include shell-style glob patterns, e.g. "ftp*.example.com".
See the documentation for more information.`,
},
"allow_wildcard_certificates": {
Type: framework.TypeBool,
Required: true,
Description: `If set, allows certificates with wildcards in
the common name to be issued, conforming to RFC 6125's Section 6.4.3; e.g.,
"*.example.net" or "b*z.example.net". See the documentation for more
information.`,
},
"allow_any_name": {
Type: framework.TypeBool,
Required: true,
Description: `If set, clients can request certificates for
any domain, regardless of allowed_domains restrictions.
See the documentation for more information.`,
},
"enforce_hostnames": {
Type: framework.TypeBool,
Required: true,
Description: `If set, only valid host names are allowed for
CN and DNS SANs, and the host part of email addresses. Defaults to true.`,
},
"allow_ip_sans": {
Type: framework.TypeBool,
Required: true,
Description: `If set, IP Subject Alternative Names are allowed.
Any valid IP is accepted and No authorization checking is performed.`,
},
"allowed_uri_sans": {
Type: framework.TypeCommaStringSlice,
Required: true,
Description: `If set, an array of allowed URIs for URI Subject Alternative Names.
Any valid URI is accepted, these values support globbing.`,
},
"allowed_uri_sans_template": {
Type: framework.TypeBool,
Required: true,
Description: `If set, Allowed URI SANs can be specified using identity template policies.
Non-templated URI SANs are also permitted.`,
},
"allowed_other_sans": {
Type: framework.TypeCommaStringSlice,
Required: true,
Description: `If set, an array of allowed other names to put in SANs. These values support globbing and must be in the format <oid>;<type>:<value>. Currently only "utf8" is a valid type. All values, including globbing values, must use this syntax, with the exception being a single "*" which allows any OID and any value (but type must still be utf8).`,
},
"allowed_serial_numbers": {
Type: framework.TypeCommaStringSlice,
Required: true,
Description: `If set, an array of allowed serial numbers to put in Subject. These values support globbing.`,
},
"allowed_user_ids": {
Type: framework.TypeCommaStringSlice,
Description: `If set, an array of allowed user-ids to put in user system login name specified here: https://www.rfc-editor.org/rfc/rfc1274#section-9.3.1`,
},
"server_flag": {
Type: framework.TypeBool,
Default: true,
Description: `If set, certificates are flagged for server auth use.
Defaults to true. See also RFC 5280 Section 4.2.1.12.`,
},
"client_flag": {
Type: framework.TypeBool,
Required: true,
Description: `If set, certificates are flagged for client auth use.
Defaults to true. See also RFC 5280 Section 4.2.1.12.`,
},
"code_signing_flag": {
Type: framework.TypeBool,
Required: true,
Description: `If set, certificates are flagged for code signing
use. Defaults to false. See also RFC 5280 Section 4.2.1.12.`,
},
"email_protection_flag": {
Type: framework.TypeBool,
Required: true,
Description: `If set, certificates are flagged for email
protection use. Defaults to false. See also RFC 5280 Section 4.2.1.12.`,
},
"key_type": {
Type: framework.TypeString,
Required: true,
Description: `The type of key to use; defaults to RSA. "rsa"
"ec", "ed25519" and "any" are the only valid values.`,
},
"key_bits": {
Type: framework.TypeInt,
Required: true,
Description: `The number of bits to use. Allowed values are
0 (universal default); with rsa key_type: 2048 (default), 3072, or
4096; with ec key_type: 224, 256 (default), 384, or 521; ignored with
ed25519.`,
},
"signature_bits": {
Type: framework.TypeInt,
Required: true,
Description: `The number of bits to use in the signature
algorithm; accepts 256 for SHA-2-256, 384 for SHA-2-384, and 512 for
SHA-2-512. Defaults to 0 to automatically detect based on key length
(SHA-2-256 for RSA keys, and matching the curve size for NIST P-Curves).`,
},
"use_pss": {
Type: framework.TypeBool,
Required: false,
Description: `Whether or not to use PSS signatures when using a
RSA key-type issuer. Defaults to false.`,
},
"key_usage": {
Type: framework.TypeCommaStringSlice,
Required: true,
Description: `A comma-separated string or list of key usages (not extended
key usages). Valid values can be found at
https://golang.org/pkg/crypto/x509/#KeyUsage
-- simply drop the "KeyUsage" part of the name.
To remove all key usages from being set, set
this value to an empty list. See also RFC 5280
Section 4.2.1.3.`,
},
"ext_key_usage": {
Type: framework.TypeCommaStringSlice,
Required: true,
Description: `A comma-separated string or list of extended key usages. Valid values can be found at
https://golang.org/pkg/crypto/x509/#ExtKeyUsage
-- simply drop the "ExtKeyUsage" part of the name.
To remove all key usages from being set, set
this value to an empty list. See also RFC 5280
Section 4.2.1.12.`,
},
"ext_key_usage_oids": {
Type: framework.TypeCommaStringSlice,
Required: true,
Description: `A comma-separated string or list of extended key usage oids.`,
},
"use_csr_common_name": {
Type: framework.TypeBool,
Required: true,
Description: `If set, when used with a signing profile,
the common name in the CSR will be used. This
does *not* include any requested Subject Alternative
Names; use use_csr_sans for that. Defaults to true.`,
},
"use_csr_sans": {
Type: framework.TypeBool,
Required: true,
Description: `If set, when used with a signing profile,
the SANs in the CSR will be used. This does *not*
include the Common Name (cn); use use_csr_common_name
for that. Defaults to true.`,
},
"ou": {
Type: framework.TypeCommaStringSlice,
Description: `If set, OU (OrganizationalUnit) will be set to
this value in certificates issued by this role.`,
},
"organization": {
Type: framework.TypeCommaStringSlice,
Description: `If set, O (Organization) will be set to
this value in certificates issued by this role.`,
},
"country": {
Type: framework.TypeCommaStringSlice,
Description: `If set, Country will be set to
this value in certificates issued by this role.`,
},
"locality": {
Type: framework.TypeCommaStringSlice,
Description: `If set, Locality will be set to
this value in certificates issued by this role.`,
},
"province": {
Type: framework.TypeCommaStringSlice,
Description: `If set, Province will be set to
this value in certificates issued by this role.`,
},
"street_address": {
Type: framework.TypeCommaStringSlice,
Description: `If set, Street Address will be set to
this value in certificates issued by this role.`,
},
"postal_code": {
Type: framework.TypeCommaStringSlice,
Description: `If set, Postal Code will be set to
this value in certificates issued by this role.`,
},
"generate_lease": {
Type: framework.TypeBool,
Description: `
If set, certificates issued/signed against this role will have Vault leases
attached to them. Defaults to "false". Certificates can be added to the CRL by
"vault revoke <lease_id>" when certificates are associated with leases. It can
also be done using the "pki/revoke" endpoint. However, when lease generation is
disabled, invoking "pki/revoke" would be the only way to add the certificates
to the CRL. When large number of certificates are generated with long
lifetimes, it is recommended that lease generation be disabled, as large amount of
leases adversely affect the startup time of Vault.`,
},
"no_store": {
Type: framework.TypeBool,
Description: `
If set, certificates issued/signed against this role will not be stored in the
storage backend. This can improve performance when issuing large numbers of
certificates. However, certificates issued in this way cannot be enumerated
or revoked, so this option is recommended only for certificates that are
non-sensitive, or extremely short-lived. This option implies a value of "false"
for "generate_lease".`,
},
"require_cn": {
Type: framework.TypeBool,
Description: `If set to false, makes the 'common_name' field optional while generating a certificate.`,
},
"cn_validations": {
Type: framework.TypeCommaStringSlice,
Description: `List of allowed validations to run against the
Common Name field. Values can include 'email' to validate the CN is a email
address, 'hostname' to validate the CN is a valid hostname (potentially
including wildcards). When multiple validations are specified, these take
OR semantics (either email OR hostname are allowed). The special value
'disabled' allows disabling all CN name validations, allowing for arbitrary
non-Hostname, non-Email address CNs.`,
},
"policy_identifiers": {
Type: framework.TypeCommaStringSlice,
Description: `A comma-separated string or list of policy OIDs, or a JSON list of qualified policy
information, which must include an oid, and may include a notice and/or cps url, using the form
[{"oid"="1.3.6.1.4.1.7.8","notice"="I am a user Notice"}, {"oid"="1.3.6.1.4.1.44947.1.2.4 ","cps"="https://example.com"}].`,
},
"basic_constraints_valid_for_non_ca": {
Type: framework.TypeBool,
Description: `Mark Basic Constraints valid when issuing non-CA certificates.`,
},
"not_before_duration": {
Type: framework.TypeDurationSecond,
Description: `The duration before now which the certificate needs to be backdated by.`,
},
"not_after": {
Type: framework.TypeString,
Description: `Set the not after field of the certificate with specified date value.
The value format should be given in UTC format YYYY-MM-ddTHH:MM:SSZ.`,
},
"issuer_ref": {
Type: framework.TypeString,
Description: `Reference to the issuer used to sign requests
serviced by this role.`,
},
}
return &framework.Path{
Pattern: "roles/" + framework.GenericNameRegex("name"),
Fields: map[string]*framework.FieldSchema{
@ -450,21 +809,44 @@ serviced by this role.`,
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Callback: b.pathRoleRead,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: pathRolesResponse,
}},
},
},
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathRoleCreate,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: pathRolesResponse,
}},
},
// Read more about why these flags are set in backend.go.
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,
},
logical.DeleteOperation: &framework.PathOperation{
Callback: b.pathRoleDelete,
Responses: map[int][]framework.Response{
http.StatusNoContent: {{
Description: "No Content",
}},
},
// Read more about why these flags are set in backend.go.
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,
},
logical.PatchOperation: &framework.PathOperation{
Callback: b.pathRolePatch,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: pathRolesResponse,
}},
},
// Read more about why these flags are set in backend.go.
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,

View File

@ -9,6 +9,8 @@ import (
"fmt"
"testing"
"github.com/hashicorp/vault/sdk/helper/testhelpers/schema"
"github.com/go-errors/errors"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/hashicorp/vault/sdk/logical"
@ -141,12 +143,14 @@ func TestPki_RoleKeyUsage(t *testing.T) {
}
resp, err = b.HandleRequest(context.Background(), roleReq)
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("roles/testrole"), logical.UpdateOperation), resp, true)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v resp: %#v", err, resp)
}
roleReq.Operation = logical.ReadOperation
resp, err = b.HandleRequest(context.Background(), roleReq)
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("roles/testrole"), logical.ReadOperation), resp, true)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v resp: %#v", err, resp)
}

View File

@ -12,6 +12,7 @@ import (
"encoding/pem"
"errors"
"fmt"
"net/http"
"reflect"
"strings"
"time"
@ -35,6 +36,11 @@ func pathDeleteRoot(b *backend) *framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.DeleteOperation: &framework.PathOperation{
Callback: b.pathCADeleteRoot,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
}},
},
// Read more about why these flags are set in backend.go
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,

View File

@ -1,6 +1,8 @@
package pki
import (
"net/http"
"github.com/hashicorp/vault/sdk/framework"
"github.com/hashicorp/vault/sdk/logical"
)
@ -23,6 +25,38 @@ func buildPathIssuerSignIntermediateRaw(b *backend, pattern string) *framework.P
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathIssuerSignIntermediate,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"expiration": {
Type: framework.TypeInt64,
Description: `Expiration Time`,
Required: true,
},
"serial_number": {
Type: framework.TypeString,
Description: `Serial Number`,
Required: false,
},
"certificate": {
Type: framework.TypeString,
Description: `Certificate`,
Required: true,
},
"issuing_ca": {
Type: framework.TypeString,
Description: `Issuing CA`,
Required: true,
},
"ca_chain": {
Type: framework.TypeStringSlice,
Description: `CA Chain`,
Required: true,
},
},
}},
},
},
},
@ -140,6 +174,23 @@ func buildPathIssuerSignSelfIssued(b *backend, pattern string) *framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathIssuerSignSelfIssued,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"certificate": {
Type: framework.TypeString,
Description: `Certificate`,
Required: true,
},
"issuing_ca": {
Type: framework.TypeString,
Description: `Issuing CA`,
Required: true,
},
},
}},
},
},
},

View File

@ -101,7 +101,13 @@ func pathTidy(b *backend) *framework.Path {
Fields: addTidyFields(map[string]*framework.FieldSchema{}),
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathTidyWrite,
Callback: b.pathTidyWrite,
Responses: map[int][]framework.Response{
http.StatusAccepted: {{
Description: "Accepted",
Fields: map[string]*framework.FieldSchema{},
}},
},
ForwardPerformanceStandby: true,
},
},
@ -115,7 +121,122 @@ func pathTidyCancel(b *backend) *framework.Path {
Pattern: "tidy-cancel$",
Operations: map[logical.Operation]framework.OperationHandler{
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathTidyCancelWrite,
Callback: b.pathTidyCancelWrite,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"safety_buffer": {
Type: framework.TypeInt,
Description: `Safety buffer time duration`,
Required: false,
},
"issuer_safety_buffer": {
Type: framework.TypeInt,
Description: `Issuer safety buffer`,
Required: false,
},
"tidy_cert_store": {
Type: framework.TypeBool,
Description: `Tidy certificate store`,
Required: false,
},
"tidy_revoked_certs": {
Type: framework.TypeBool,
Description: `Tidy revoked certificates`,
Required: false,
},
"tidy_revoked_cert_issuer_associations": {
Type: framework.TypeBool,
Description: `Tidy revoked certificate issuer associations`,
Required: false,
},
"tidy_expired_issuers": {
Type: framework.TypeBool,
Description: `Tidy expired issuers`,
Required: false,
},
"pause_duration": {
Type: framework.TypeString,
Description: `Duration to pause between tidying certificates`,
Required: false,
},
"state": {
Type: framework.TypeString,
Description: `One of Inactive, Running, Finished, or Error`,
Required: false,
},
"error": {
Type: framework.TypeString,
Description: `The error message`,
Required: false,
},
"time_started": {
Type: framework.TypeString,
Description: `Time the operation started`,
Required: false,
},
"time_finished": {
Type: framework.TypeString,
Description: `Time the operation finished`,
Required: false,
},
"message": {
Type: framework.TypeString,
Description: `Message of the operation`,
Required: false,
},
"cert_store_deleted_count": {
Type: framework.TypeInt,
Description: `The number of certificate storage entries deleted`,
Required: false,
},
"revoked_cert_deleted_count": {
Type: framework.TypeInt,
Description: `The number of revoked certificate entries deleted`,
Required: false,
},
"current_cert_store_count": {
Type: framework.TypeInt,
Description: `The number of revoked certificate entries deleted`,
Required: false,
},
"current_revoked_cert_count": {
Type: framework.TypeInt,
Description: `The number of revoked certificate entries deleted`,
Required: false,
},
"missing_issuer_cert_count": {
Type: framework.TypeInt,
Required: false,
},
"tidy_move_legacy_ca_bundle": {
Type: framework.TypeBool,
Required: false,
},
"tidy_cross_cluster_revoked_certs": {
Type: framework.TypeBool,
Required: false,
},
"tidy_revocation_queue": {
Type: framework.TypeBool,
Required: false,
},
"revocation_queue_deleted_count": {
Type: framework.TypeInt,
Required: false,
},
"cross_revoked_cert_deleted_count": {
Type: framework.TypeInt,
Required: false,
},
"internal_backend_uuid": {
Type: framework.TypeString,
Required: false,
},
},
}},
},
ForwardPerformanceStandby: true,
},
},
@ -129,7 +250,124 @@ func pathTidyStatus(b *backend) *framework.Path {
Pattern: "tidy-status$",
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Callback: b.pathTidyStatusRead,
Callback: b.pathTidyStatusRead,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"safety_buffer": {
Type: framework.TypeInt,
Description: `Safety buffer time duration`,
Required: true,
},
"issuer_safety_buffer": {
Type: framework.TypeInt,
Description: `Issuer safety buffer`,
Required: true,
},
"tidy_cert_store": {
Type: framework.TypeBool,
Description: `Tidy certificate store`,
Required: true,
},
"tidy_revoked_certs": {
Type: framework.TypeBool,
Description: `Tidy revoked certificates`,
Required: true,
},
"tidy_revoked_cert_issuer_associations": {
Type: framework.TypeBool,
Description: `Tidy revoked certificate issuer associations`,
Required: true,
},
"tidy_expired_issuers": {
Type: framework.TypeBool,
Description: `Tidy expired issuers`,
Required: true,
},
"tidy_cross_cluster_revoked_certs": {
Type: framework.TypeString,
Description: ``,
Required: false,
},
"pause_duration": {
Type: framework.TypeString,
Description: `Duration to pause between tidying certificates`,
Required: true,
},
"state": {
Type: framework.TypeString,
Description: `One of Inactive, Running, Finished, or Error`,
Required: true,
},
"error": {
Type: framework.TypeString,
Description: `The error message`,
Required: true,
},
"time_started": {
Type: framework.TypeString,
Description: `Time the operation started`,
Required: true,
},
"time_finished": {
Type: framework.TypeString,
Description: `Time the operation finished`,
Required: true,
},
"message": {
Type: framework.TypeString,
Description: `Message of the operation`,
Required: true,
},
"cert_store_deleted_count": {
Type: framework.TypeInt,
Description: `The number of certificate storage entries deleted`,
Required: true,
},
"revoked_cert_deleted_count": {
Type: framework.TypeInt,
Description: `The number of revoked certificate entries deleted`,
Required: true,
},
"current_cert_store_count": {
Type: framework.TypeInt,
Description: `The number of revoked certificate entries deleted`,
Required: true,
},
"cross_revoked_cert_deleted_count": {
Type: framework.TypeInt,
Description: ``,
Required: true,
},
"current_revoked_cert_count": {
Type: framework.TypeInt,
Description: `The number of revoked certificate entries deleted`,
Required: true,
},
"revocation_queue_deleted_count": {
Type: framework.TypeInt,
Required: true,
},
"tidy_move_legacy_ca_bundle": {
Type: framework.TypeBool,
Required: true,
},
"tidy_revocation_queue": {
Type: framework.TypeBool,
Required: true,
},
"missing_issuer_cert_count": {
Type: framework.TypeInt,
Required: true,
},
"internal_backend_uuid": {
Type: framework.TypeString,
Required: true,
},
},
}},
},
ForwardPerformanceStandby: true,
},
},
@ -155,9 +393,153 @@ func pathConfigAutoTidy(b *backend) *framework.Path {
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Callback: b.pathConfigAutoTidyRead,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"enabled": {
Type: framework.TypeBool,
Description: `Specifies whether automatic tidy is enabled or not`,
Required: true,
},
"interval_duration": {
Type: framework.TypeInt,
Description: `Specifies the duration between automatic tidy operation`,
Required: true,
},
"tidy_cert_store": {
Type: framework.TypeBool,
Description: `Specifies whether to tidy up the certificate store`,
Required: true,
},
"tidy_revoked_certs": {
Type: framework.TypeBool,
Description: `Specifies whether to remove all invalid and expired certificates from storage`,
Required: true,
},
"tidy_revoked_cert_issuer_associations": {
Type: framework.TypeBool,
Description: `Specifies whether to associate revoked certificates with their corresponding issuers`,
Required: true,
},
"tidy_expired_issuers": {
Type: framework.TypeBool,
Description: `Specifies whether tidy expired issuers`,
Required: true,
},
"safety_buffer": {
Type: framework.TypeInt,
Description: `Safety buffer time duration`,
Required: true,
},
"issuer_safety_buffer": {
Type: framework.TypeInt,
Description: `Issuer safety buffer`,
Required: true,
},
"pause_duration": {
Type: framework.TypeString,
Description: `Duration to pause between tidying certificates`,
Required: true,
},
"tidy_move_legacy_ca_bundle": {
Type: framework.TypeBool,
Required: true,
},
"tidy_cross_cluster_revoked_certs": {
Type: framework.TypeBool,
Required: true,
},
"tidy_revocation_queue": {
Type: framework.TypeBool,
Required: true,
},
"revocation_queue_safety_buffer": {
Type: framework.TypeDurationSecond,
Required: true,
},
"publish_stored_certificate_count_metrics": {
Type: framework.TypeBool,
Required: true,
},
"maintain_stored_certificate_counts": {
Type: framework.TypeBool,
Required: true,
},
},
}},
},
},
logical.UpdateOperation: &framework.PathOperation{
Callback: b.pathConfigAutoTidyWrite,
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"enabled": {
Type: framework.TypeBool,
Description: `Specifies whether automatic tidy is enabled or not`,
Required: true,
},
"interval_duration": {
Type: framework.TypeInt,
Description: `Specifies the duration between automatic tidy operation`,
Required: true,
},
"tidy_cert_store": {
Type: framework.TypeBool,
Description: `Specifies whether to tidy up the certificate store`,
Required: true,
},
"tidy_revoked_certs": {
Type: framework.TypeBool,
Description: `Specifies whether to remove all invalid and expired certificates from storage`,
Required: true,
},
"tidy_revoked_cert_issuer_associations": {
Type: framework.TypeBool,
Description: `Specifies whether to associate revoked certificates with their corresponding issuers`,
Required: true,
},
"tidy_expired_issuers": {
Type: framework.TypeBool,
Description: `Specifies whether tidy expired issuers`,
Required: true,
},
"safety_buffer": {
Type: framework.TypeInt,
Description: `Safety buffer time duration`,
Required: true,
},
"issuer_safety_buffer": {
Type: framework.TypeInt,
Description: `Issuer safety buffer`,
Required: true,
},
"pause_duration": {
Type: framework.TypeString,
Description: `Duration to pause between tidying certificates`,
Required: true,
},
"tidy_cross_cluster_revoked_certs": {
Type: framework.TypeBool,
Required: true,
},
"tidy_revocation_queue": {
Type: framework.TypeBool,
Required: true,
},
"tidy_move_legacy_ca_bundle": {
Type: framework.TypeBool,
Required: true,
},
"revocation_queue_safety_buffer": {
Type: framework.TypeDurationSecond,
Required: true,
},
},
}},
},
// Read more about why these flags are set in backend.go.
ForwardPerformanceStandby: true,
ForwardPerformanceSecondary: true,

View File

@ -8,6 +8,7 @@ import (
"time"
"github.com/hashicorp/vault/helper/testhelpers"
"github.com/hashicorp/vault/sdk/helper/testhelpers/schema"
"github.com/armon/go-metrics"
@ -224,17 +225,19 @@ func TestTidyCancellation(t *testing.T) {
// Kick off a tidy operation (which runs in the background), but with
// a slow-ish pause between certificates.
_, err = CBWrite(b, s, "tidy", map[string]interface{}{
resp, err := CBWrite(b, s, "tidy", map[string]interface{}{
"tidy_cert_store": true,
"safety_buffer": "1s",
"pause_duration": "1s",
})
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("tidy"), logical.UpdateOperation), resp, true)
// If we wait six seconds, the operation should still be running. That's
// how we check that pause_duration works.
time.Sleep(3 * time.Second)
resp, err := CBRead(b, s, "tidy-status")
resp, err = CBRead(b, s, "tidy-status")
require.NoError(t, err)
require.NotNil(t, resp)
require.NotNil(t, resp.Data)
@ -242,6 +245,7 @@ func TestTidyCancellation(t *testing.T) {
// If we now cancel the operation, the response should say Cancelling.
cancelResp, err := CBWrite(b, s, "tidy-cancel", map[string]interface{}{})
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("tidy-cancel"), logical.UpdateOperation), resp, true)
require.NoError(t, err)
require.NotNil(t, cancelResp)
require.NotNil(t, cancelResp.Data)
@ -261,6 +265,7 @@ func TestTidyCancellation(t *testing.T) {
time.Sleep(3 * time.Second)
statusResp, err := CBRead(b, s, "tidy-status")
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("tidy-status"), logical.ReadOperation), resp, true)
require.NoError(t, err)
require.NotNil(t, statusResp)
require.NotNil(t, statusResp.Data)
@ -385,6 +390,7 @@ func TestTidyIssuerConfig(t *testing.T) {
// Ensure the default auto-tidy config matches expectations
resp, err := CBRead(b, s, "config/auto-tidy")
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("config/auto-tidy"), logical.ReadOperation), resp, true)
requireSuccessNonNilResponse(t, resp, err)
jsonBlob, err := json.Marshal(&defaultTidyConfig)
@ -407,6 +413,8 @@ func TestTidyIssuerConfig(t *testing.T) {
"tidy_expired_issuers": true,
"issuer_safety_buffer": "5s",
})
schema.ValidateResponse(t, schema.GetResponseSchema(t, b.Route("config/auto-tidy"), logical.UpdateOperation), resp, true)
requireSuccessNonNilResponse(t, resp, err)
require.Equal(t, true, resp.Data["tidy_expired_issuers"])
require.Equal(t, 5, resp.Data["issuer_safety_buffer"])