Adds CertificateRequest approval condition validation to ensure:

- Only a single Approve _or_ Deny condition may exist
- They cannot be modified once set
- They must always have a status of `True`

Signed-off-by: joshvanl <vleeuwenjoshua@gmail.com>
This commit is contained in:
joshvanl 2021-03-08 13:32:59 +00:00
parent 5df29e41e7
commit 4e042011e6
2 changed files with 372 additions and 91 deletions

View File

@ -31,6 +31,7 @@ import (
"github.com/jetstack/cert-manager/pkg/apis/acme"
"github.com/jetstack/cert-manager/pkg/apis/certmanager"
cmapi "github.com/jetstack/cert-manager/pkg/internal/apis/certmanager"
cmmeta "github.com/jetstack/cert-manager/pkg/internal/apis/meta"
"github.com/jetstack/cert-manager/pkg/util"
"github.com/jetstack/cert-manager/pkg/util/pki"
)
@ -40,6 +41,9 @@ var defaultInternalKeyUsages = []cmapi.KeyUsage{cmapi.UsageDigitalSignature, cma
func ValidateCertificateRequest(_ *admissionv1.AdmissionRequest, obj runtime.Object) field.ErrorList {
cr := obj.(*cmapi.CertificateRequest)
allErrs := ValidateCertificateRequestSpec(&cr.Spec, field.NewPath("spec"), true)
allErrs = append(allErrs,
ValidateCertificateRequestApprovalCondition(cr.Status.Conditions, field.NewPath("status", "conditions"))...)
return allErrs
}
@ -55,6 +59,8 @@ func ValidateUpdateCertificateRequest(_ *admissionv1.AdmissionRequest, oldObj, n
annotationField := field.NewPath("metadata", "annotations")
el = append(el, validateCertificateRequestAnnotations(oldCR, newCR, annotationField)...)
el = append(el, validateCertificateRequestAnnotations(newCR, oldCR, annotationField)...)
el = append(el,
ValidateUpdateCertificateRequestApprovalCondition(oldCR.Status.Conditions, newCR.Status.Conditions, field.NewPath("status", "conditions"))...)
if !reflect.DeepEqual(oldCR.Spec, newCR.Spec) {
el = append(el, field.Forbidden(field.NewPath("spec"), "cannot change spec after creation"))
@ -107,6 +113,80 @@ func ValidateCertificateRequestSpec(crSpec *cmapi.CertificateRequestSpec, fldPat
return el
}
// ValidateCertificateRequestApprovalCondition will ensure that only a single
// 'Approved' or 'Denied' condition may exist, and that they are set to True.
func ValidateCertificateRequestApprovalCondition(crConds []cmapi.CertificateRequestCondition, fldPath *field.Path) field.ErrorList {
var (
approvedConditions []cmapi.CertificateRequestCondition
deniedConditions []cmapi.CertificateRequestCondition
el = field.ErrorList{}
)
for _, cond := range crConds {
if cond.Type == cmapi.CertificateRequestConditionApproved {
approvedConditions = append(approvedConditions, cond)
}
if cond.Type == cmapi.CertificateRequestConditionDenied {
deniedConditions = append(deniedConditions, cond)
}
}
for _, cond := range []struct {
condType cmapi.CertificateRequestConditionType
conditions []cmapi.CertificateRequestCondition
}{
{cmapi.CertificateRequestConditionApproved, approvedConditions},
{cmapi.CertificateRequestConditionDenied, deniedConditions},
} {
switch len(cond.conditions) {
case 0:
break
case 1:
if condition := cond.conditions[0]; condition.Status != cmmeta.ConditionTrue {
el = append(el, field.Invalid(fldPath.Child(condition.Reason), condition.Status,
fmt.Sprintf("%q condition may only be set to True", cond.condType)))
}
default:
el = append(el, field.Forbidden(fldPath, fmt.Sprintf("multiple %q conditions present", cond.condType)))
}
}
if len(deniedConditions) > 0 && len(approvedConditions) > 0 {
el = append(el, field.Forbidden(fldPath, "both 'Denied' and 'Approved' conditions cannot coexist"))
}
return el
}
// ValidateUpdateCertificateRequestApprovalCondition will ensure that the
// 'Approved' and 'Denied' conditions may not be changed once set, i.e. if they
// exist, they are not modified in the updated resource. Also runs the base
// approval validation on the updated CertificateRequest conditions.
func ValidateUpdateCertificateRequestApprovalCondition(oldCRConds, newCRConds []cmapi.CertificateRequestCondition, fldPath *field.Path) field.ErrorList {
var (
el = field.ErrorList{}
oldCRDenied = getCertificateRequestCondition(oldCRConds, cmapi.CertificateRequestConditionDenied)
oldCRApproved = getCertificateRequestCondition(oldCRConds, cmapi.CertificateRequestConditionApproved)
)
// If the approval condition has been set, ensure it hasn't been modified.
if oldCRApproved != nil && !reflect.DeepEqual(oldCRApproved,
getCertificateRequestCondition(newCRConds, cmapi.CertificateRequestConditionApproved),
) {
el = append(el, field.Forbidden(fldPath, "'Approved' condition may not be modified once set"))
}
// If the denied condition has been set, ensure it hasn't been modified.
if oldCRDenied != nil && !reflect.DeepEqual(oldCRDenied,
getCertificateRequestCondition(newCRConds, cmapi.CertificateRequestConditionDenied),
) {
el = append(el, field.Forbidden(fldPath, "'Denied' condition may not be modified once set"))
}
return append(el, ValidateCertificateRequestApprovalCondition(newCRConds, fldPath)...)
}
func getCSRKeyUsage(crSpec *cmapi.CertificateRequestSpec, fldPath *field.Path, csr *x509.CertificateRequest, el field.ErrorList) ([]cmapi.KeyUsage, field.ErrorList) {
var ekus []x509.ExtKeyUsage
var ku x509.KeyUsage
@ -200,3 +280,12 @@ func ensureCertSignIsSet(list []cmapi.KeyUsage) []cmapi.KeyUsage {
return append(list, cmapi.UsageCertSign)
}
func getCertificateRequestCondition(conds []cmapi.CertificateRequestCondition, conditionType cmapi.CertificateRequestConditionType) *cmapi.CertificateRequestCondition {
for _, cond := range conds {
if cond.Type == conditionType {
return &cond
}
}
return nil
}

View File

@ -27,6 +27,7 @@ import (
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
cminternal "github.com/jetstack/cert-manager/pkg/internal/apis/certmanager"
cminternalmeta "github.com/jetstack/cert-manager/pkg/internal/apis/meta"
"github.com/jetstack/cert-manager/pkg/util/pki"
utilpki "github.com/jetstack/cert-manager/pkg/util/pki"
"github.com/jetstack/cert-manager/test/unit/gen"
@ -114,122 +115,318 @@ func TestValidateCertificateRequestUpdate(t *testing.T) {
}
func TestValidateCertificateRequestSpec(t *testing.T) {
fldPath := field.NewPath("test")
fldPath := field.NewPath("spec")
fldPathConditions := field.NewPath("status", "conditions")
tests := []struct {
name string
crSpec *cminternal.CertificateRequestSpec
want field.ErrorList
tests := map[string]struct {
cr *cminternal.CertificateRequest
want field.ErrorList
}{
{
name: "Test csr with no usages",
crSpec: &cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("test", gen.SetCertificateDNSNames("example.com"))),
IssuerRef: validIssuerRef,
Usages: nil,
"Test csr with no usages": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"))),
IssuerRef: validIssuerRef,
Usages: nil,
},
},
want: []*field.Error{},
},
{
name: "Test csr with double signature usages",
crSpec: &cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("test", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageSigning, cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment))),
IssuerRef: validIssuerRef,
Usages: []cminternal.KeyUsage{cminternal.UsageSigning, cminternal.UsageKeyEncipherment},
"Test csr with double signature usages": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageSigning, cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment))),
IssuerRef: validIssuerRef,
Usages: []cminternal.KeyUsage{cminternal.UsageSigning, cminternal.UsageKeyEncipherment},
},
},
want: []*field.Error{},
},
{
name: "Test csr with double extended usages",
crSpec: &cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("test", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment, cmapi.UsageServerAuth, cmapi.UsageClientAuth))),
IssuerRef: validIssuerRef,
Usages: []cminternal.KeyUsage{cminternal.UsageSigning, cminternal.UsageKeyEncipherment, cminternal.UsageServerAuth, cminternal.UsageClientAuth},
"Test csr with double extended usages": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment, cmapi.UsageServerAuth, cmapi.UsageClientAuth))),
IssuerRef: validIssuerRef,
Usages: []cminternal.KeyUsage{cminternal.UsageSigning, cminternal.UsageKeyEncipherment, cminternal.UsageServerAuth, cminternal.UsageClientAuth},
},
},
want: []*field.Error{},
},
{
name: "Test csr with reordered usages",
crSpec: &cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("test", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment, cmapi.UsageServerAuth, cmapi.UsageClientAuth))),
IssuerRef: validIssuerRef,
Usages: []cminternal.KeyUsage{cminternal.UsageServerAuth, cminternal.UsageClientAuth, cminternal.UsageKeyEncipherment, cminternal.UsageDigitalSignature},
"Test csr with reordered usages": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment, cmapi.UsageServerAuth, cmapi.UsageClientAuth))),
IssuerRef: validIssuerRef,
Usages: []cminternal.KeyUsage{cminternal.UsageServerAuth, cminternal.UsageClientAuth, cminternal.UsageKeyEncipherment, cminternal.UsageDigitalSignature},
},
},
want: []*field.Error{},
},
{
name: "Test csr that is CA with usages set",
crSpec: &cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("test", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny, cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment, cmapi.UsageCertSign), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny, cminternal.UsageDigitalSignature, cminternal.UsageKeyEncipherment, cminternal.UsageCertSign},
"Test csr that is CA with usages set": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny, cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment, cmapi.UsageCertSign), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny, cminternal.UsageDigitalSignature, cminternal.UsageKeyEncipherment, cminternal.UsageCertSign},
},
},
want: []*field.Error{},
},
{
name: "Test csr that is CA but no cert sign in usages",
crSpec: &cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("test", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny, cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment, cmapi.UsageClientAuth, cmapi.UsageServerAuth), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny, cminternal.UsageDigitalSignature, cminternal.UsageKeyEncipherment, cminternal.UsageClientAuth, cminternal.UsageServerAuth},
"Test csr that is CA but no cert sign in usages": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny, cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment, cmapi.UsageClientAuth, cmapi.UsageServerAuth), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny, cminternal.UsageDigitalSignature, cminternal.UsageKeyEncipherment, cminternal.UsageClientAuth, cminternal.UsageServerAuth},
},
},
want: []*field.Error{},
},
{
name: "Error on csr not having all usages",
crSpec: &cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("test", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment, cmapi.UsageServerAuth))),
IssuerRef: validIssuerRef,
Usages: []cminternal.KeyUsage{cminternal.UsageSigning, cminternal.UsageKeyEncipherment, cminternal.UsageServerAuth, cminternal.UsageClientAuth},
"Error on csr not having all usages": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment, cmapi.UsageServerAuth))),
IssuerRef: validIssuerRef,
Usages: []cminternal.KeyUsage{cminternal.UsageSigning, cminternal.UsageKeyEncipherment, cminternal.UsageServerAuth, cminternal.UsageClientAuth},
},
},
want: []*field.Error{
field.Invalid(fldPath.Child("request"), nil, "csr key usages do not match specified usages, these should match if both are set: [[]certmanager.KeyUsage[3] != []certmanager.KeyUsage[4]]"),
},
},
{
name: "Error on cr not having all usages",
crSpec: &cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("test", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment, cmapi.UsageServerAuth, cmapi.UsageClientAuth))),
IssuerRef: validIssuerRef,
Usages: []cminternal.KeyUsage{cminternal.UsageSigning, cminternal.UsageKeyEncipherment},
"Error on cr not having all usages": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment, cmapi.UsageServerAuth, cmapi.UsageClientAuth))),
IssuerRef: validIssuerRef,
Usages: []cminternal.KeyUsage{cminternal.UsageSigning, cminternal.UsageKeyEncipherment},
},
},
want: []*field.Error{
field.Invalid(fldPath.Child("request"), nil, "csr key usages do not match specified usages, these should match if both are set: [[]certmanager.KeyUsage[4] != []certmanager.KeyUsage[2]]"),
},
},
{
name: "Error on cr not having all usages",
crSpec: &cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("test", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageDigitalSignature, cmapi.UsageKeyEncipherment, cmapi.UsageServerAuth, cmapi.UsageClientAuth))),
IssuerRef: validIssuerRef,
Usages: []cminternal.KeyUsage{cminternal.UsageAny, cminternal.UsageSigning},
},
want: []*field.Error{
field.Invalid(fldPath.Child("request"), nil, "csr key usages do not match specified usages, these should match if both are set: [[]certmanager.KeyUsage[4] != []certmanager.KeyUsage[2]]"),
},
},
{
name: "Test csr with any, signing, digital signature, key encipherment, server and client auth",
crSpec: &cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("test", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny, cmapi.UsageSigning, cmapi.UsageKeyEncipherment, cmapi.UsageClientAuth, cmapi.UsageServerAuth), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny, cminternal.UsageSigning, cminternal.UsageKeyEncipherment, cminternal.UsageClientAuth, cminternal.UsageServerAuth},
"Test csr with any, signing, digital signature, key encipherment, server and client auth": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny, cmapi.UsageSigning, cmapi.UsageKeyEncipherment, cmapi.UsageClientAuth, cmapi.UsageServerAuth), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny, cminternal.UsageSigning, cminternal.UsageKeyEncipherment, cminternal.UsageClientAuth, cminternal.UsageServerAuth},
},
},
want: []*field.Error{},
},
"CertificateRequest with single Approved=true condition, shouldn't error": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny},
},
Status: cminternal.CertificateRequestStatus{
Conditions: []cminternal.CertificateRequestCondition{
{
Type: cminternal.CertificateRequestConditionApproved,
Status: cminternalmeta.ConditionTrue,
},
},
},
},
want: []*field.Error{},
},
"CertificateRequest with single Denied=true condition, shouldn't error": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny},
},
Status: cminternal.CertificateRequestStatus{
Conditions: []cminternal.CertificateRequestCondition{
{
Type: cminternal.CertificateRequestConditionDenied,
Status: cminternalmeta.ConditionTrue,
},
},
},
},
want: []*field.Error{},
},
"CertificateRequest with single Approved=false condition, should error": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny},
},
Status: cminternal.CertificateRequestStatus{
Conditions: []cminternal.CertificateRequestCondition{
{
Type: cminternal.CertificateRequestConditionApproved,
Status: cminternalmeta.ConditionFalse,
Reason: cmapi.CertificateRequestReasonApproved,
},
},
},
},
want: []*field.Error{
field.Invalid(fldPathConditions.Child(cmapi.CertificateRequestReasonApproved), nil,
`"Approved" condition may only be set to True`),
},
},
"CertificateRequest with single Denied=false condition, should error": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny},
},
Status: cminternal.CertificateRequestStatus{
Conditions: []cminternal.CertificateRequestCondition{
{
Type: cminternal.CertificateRequestConditionDenied,
Status: cminternalmeta.ConditionFalse,
Reason: cmapi.CertificateRequestReasonDenied,
},
},
},
},
want: []*field.Error{
field.Invalid(fldPathConditions.Child(cmapi.CertificateRequestReasonDenied), nil,
`"Denied" condition may only be set to True`),
},
},
"CertificateRequest with both Denied=false and Approved=false conditions, should error": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny},
},
Status: cminternal.CertificateRequestStatus{
Conditions: []cminternal.CertificateRequestCondition{
{
Type: cminternal.CertificateRequestConditionApproved,
Status: cminternalmeta.ConditionFalse,
Reason: cmapi.CertificateRequestReasonApproved,
},
{
Type: cminternal.CertificateRequestConditionDenied,
Status: cminternalmeta.ConditionFalse,
Reason: cmapi.CertificateRequestReasonDenied,
},
},
},
},
want: []*field.Error{
field.Invalid(field.NewPath("status", "conditions", cmapi.CertificateRequestReasonApproved), nil,
`"Approved" condition may only be set to True`),
field.Invalid(field.NewPath("status", "conditions", cmapi.CertificateRequestReasonDenied), nil,
`"Denied" condition may only be set to True`),
field.Forbidden(fldPathConditions, "both 'Denied' and 'Approved' conditions cannot coexist"),
},
},
"CertificateRequest with both Denied=true and Approved=true conditions, should error": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny},
},
Status: cminternal.CertificateRequestStatus{
Conditions: []cminternal.CertificateRequestCondition{
{
Type: cminternal.CertificateRequestConditionApproved,
Status: cminternalmeta.ConditionTrue,
Reason: cmapi.CertificateRequestReasonApproved,
},
{
Type: cminternal.CertificateRequestConditionDenied,
Status: cminternalmeta.ConditionTrue,
Reason: cmapi.CertificateRequestReasonDenied,
},
},
},
},
want: []*field.Error{
field.Forbidden(fldPathConditions, "both 'Denied' and 'Approved' conditions cannot coexist"),
},
},
"CertificateRequest with multiple Approved conditions, should error": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny},
},
Status: cminternal.CertificateRequestStatus{
Conditions: []cminternal.CertificateRequestCondition{
{
Type: cminternal.CertificateRequestConditionApproved,
Status: cminternalmeta.ConditionTrue,
Reason: cmapi.CertificateRequestReasonApproved,
},
{
Type: cminternal.CertificateRequestConditionApproved,
Status: cminternalmeta.ConditionFalse,
Reason: "foo",
},
},
},
},
want: []*field.Error{
field.Forbidden(fldPathConditions, `multiple "Approved" conditions present`),
},
},
"CertificateRequest with multiple Denied conditions, should error": {
cr: &cminternal.CertificateRequest{
Spec: cminternal.CertificateRequestSpec{
Request: mustGenerateCSR(t, gen.Certificate("spec", gen.SetCertificateDNSNames("example.com"), gen.SetCertificateKeyUsages(cmapi.UsageAny), gen.SetCertificateIsCA(true))),
IssuerRef: validIssuerRef,
IsCA: true,
Usages: []cminternal.KeyUsage{cminternal.UsageAny},
},
Status: cminternal.CertificateRequestStatus{
Conditions: []cminternal.CertificateRequestCondition{
{
Type: cminternal.CertificateRequestConditionDenied,
Status: cminternalmeta.ConditionTrue,
Reason: cmapi.CertificateRequestReasonDenied,
},
{
Type: cminternal.CertificateRequestConditionDenied,
Status: cminternalmeta.ConditionFalse,
Reason: "foo",
},
},
},
},
want: []*field.Error{
field.Forbidden(fldPathConditions, `multiple "Denied" conditions present`),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ValidateCertificateRequestSpec(tt.crSpec, field.NewPath("test"), true)
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
got := ValidateCertificateRequest(nil, tt.cr)
for i := range got {
// filter out the value so it does not print the full CSR in tests
got[i].BadValue = nil
if got[i].Type != field.ErrorTypeForbidden {
// filter out the value so it does not print the full CSR in tests
got[i].BadValue = nil
}
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ValidateCertificateRequestSpec() = %v, want %v", got, tt.want)
t.Errorf("ValidateCertificateRequest() = %v, want %v", got, tt.want)
}
})
}
@ -261,34 +458,29 @@ func mustGenerateCSR(t *testing.T, crt *cmapi.Certificate) []byte {
}
func Test_patchDuplicateKeyUsage(t *testing.T) {
tests := []struct {
name string
tests := map[string]struct {
usages []cminternal.KeyUsage
want []cminternal.KeyUsage
}{
{
name: "Test single KU",
"Test single KU": {
usages: []cminternal.KeyUsage{cminternal.UsageKeyEncipherment},
want: []cminternal.KeyUsage{cminternal.UsageKeyEncipherment},
},
{
name: "Test UsageSigning",
"Test UsageSigning": {
usages: []cminternal.KeyUsage{cminternal.UsageSigning},
want: []cminternal.KeyUsage{cminternal.UsageDigitalSignature},
},
{
name: "Test multiple KU",
"Test multiple KU": {
usages: []cminternal.KeyUsage{cminternal.UsageDigitalSignature, cminternal.UsageServerAuth, cminternal.UsageClientAuth},
want: []cminternal.KeyUsage{cminternal.UsageDigitalSignature, cminternal.UsageServerAuth, cminternal.UsageClientAuth},
},
{
name: "Test double signing",
"Test double signing": {
usages: []cminternal.KeyUsage{cminternal.UsageSigning, cminternal.UsageDigitalSignature},
want: []cminternal.KeyUsage{cminternal.UsageDigitalSignature},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
if got := patchDuplicateKeyUsage(tt.usages); !reflect.DeepEqual(got, tt.want) {
t.Errorf("patchDuplicateKeyUsage() = %v, want %v", got, tt.want)
}