Skip to content

fix(certificate-authority): Optional+Computed audit — properties diff suppression, config-constraint validation, null-safe auth-cert metadata#186

Open
spbsoluble wants to merge 4 commits into
fix/certificate-owner-role-clearfrom
fix/ca-optional-computed-audit
Open

fix(certificate-authority): Optional+Computed audit — properties diff suppression, config-constraint validation, null-safe auth-cert metadata#186
spbsoluble wants to merge 4 commits into
fix/certificate-owner-role-clearfrom
fix/ca-optional-computed-audit

Conversation

@spbsoluble

Copy link
Copy Markdown
Collaborator

Stacked on #184 (merge that first; this diff is only the last four commits). Findings come from an attribute-by-attribute Optional/Computed audit of keyfactor_certificate_authority, the follow-up to the schedule-contract work in #182.

Fixes

  • properties no longer shows a permanent diff when Command reformats stored JSON. Command's GET re-serializes the stored properties blob (key order/whitespace), so a declared value could diff on every plan even though nothing changed. A new plan modifier keeps the prior state value when plan and state are semantically equal JSON; genuine value changes still surface.
  • Documented config constraints are now actually validated at plan time instead of failing server-side (or silently misbehaving) at apply:
    • enforce_unique_dn and new_end_entity_on_renew_and_reissue cannot both be true
    • new_end_entity_on_renew_and_reissue = false is rejected for HTTPS CAs (ca_type = 1)
    • allowed_enrollment_types, use_allowed_requesters, and allowed_requesters are rejected when standalone = false
      Null/unknown values never error — only explicitly configured violations are rejected.
  • Auth-certificate metadata reads as null when the CA has no auth certificate. auth_certificate_issued_dn / auth_certificate_issuer_dn / auth_certificate_thumbprint previously landed in state as known empty strings.
  • Docs: write-only secrets clear on omission. explicit_password, auth_certificate, auth_certificate_password, and client_secret are not preserved when removed from config — the full-replace update omits them and Command clears the stored credential. The attribute descriptions now say so explicitly, since this intentionally differs from the omit=unmanaged contract the rest of the resource follows.

Verification

  • New unit tests per finding, including a red-baseline test that runs the pre-fix UseStateForUnknown modifier against the reordered-JSON scenario and asserts it does not suppress the diff (proving the new modifier is load-bearing).
  • Red→green confirmed empirically for the behavior changes: with the modifier/validators neutered, the corresponding tests fail; restored, they pass.
  • Full unit suite: 200 pass, 3 known skips, 0 fail.

Audited but intentionally untouched

  • Every other Computed attribute already has a correct plan modifier and every pointer field in the Read path already routes through nil-safe helpers — the fix: resolve 29 inconsistent-state findings from resource-wide audit #177fix(certificate-authority): preserve Daily schedule variant on read/update #182 hardening covered them; the CHANGELOG's "unaudited zero-value coercion" caveat for this resource is updated accordingly.
  • The certificate resource's standalone-CA enrollment path (certificate_authority set with neither certificate_template nor certificate_enrollment_pattern) is rejected by the at-least-one-of validator despite the schema description promising it works. That is a behavior-changing fix on a different resource and needs its own design discussion/PR — deliberately not bundled here.

…mented config constraints

properties is Optional+Computed with a bare UseStateForUnknown plan
modifier; when Command re-serializes the stored JSON with different key
order or whitespace than what's declared in config, the plan showed a
permanent diff (and Update kept resending the reformatted value) even
though the value was semantically unchanged. Wires up the previously-dead
normalizePropertiesJSON helper into a new plan modifier that keeps state
when the two are semantically equal.

Also enforces three config-time constraints that were previously only
descriptive text in this resource's attribute docs, never actually
checked: enforce_unique_dn and new_end_entity_on_renew_and_reissue are
mutually exclusive; new_end_entity_on_renew_and_reissue must not be
explicitly false for HTTPS CAs (ca_type=1); and
allowed_enrollment_types/use_allowed_requesters/allowed_requesters are
rejected when standalone is explicitly false. Undeclared or unknown
values are never treated as violations, since plan-time validation
can't resolve values that aren't known yet.
…en absent

caResponseToState only set auth_certificate_issued_dn/
auth_certificate_issuer_dn/auth_certificate_thumbprint inside the
"resp.AuthCertificate != nil" branch, so a CA with no auth certificate
configured left those three fields at the Go zero-value types.String{}
(a known empty string) instead of Null, unlike every other pointer field
in this function which already routes through a null-safe helper.
…ission

explicit_password, auth_certificate, auth_certificate_password, and
client_secret are Optional (not Computed) and, unlike this resource's
other attributes, are not preserved when omitted from config: the
full-replace update simply leaves them out of the request, which clears
the stored credential server-side on the next apply. Documents this
divergence from the resource's usual omit=unmanaged contract directly
on each attribute.
…it fixes

Adds entries for the properties JSON-normalization diff fix, the auth
certificate metadata null-branch fix, and the new plan-time config
constraint validation. Updates the "Known open gap" pending item to
reflect that the two concrete residuals found in this pass are now
fixed, rather than leaving the vague "unaudited" phrasing in place.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant