Skip to content

Commit

Permalink
Merge pull request #237 from vmware/feat/certificate-datasource
Browse files Browse the repository at this point in the history
feat: add `d/certificate` datasource
  • Loading branch information
insidepacket authored Sep 1, 2024
2 parents 0b6bd8a + 57369a6 commit fb8d578
Show file tree
Hide file tree
Showing 8 changed files with 540 additions and 58 deletions.
58 changes: 58 additions & 0 deletions docs/data-sources/certificate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "vcf_certificate Data Source - terraform-provider-vcf"
subcategory: ""
description: |-
Datasource used to extract certificate details based on fields `domain_id` and `resource_fqdn`.
---

# vcf_certificate (Data Source)

Datasource used to extract certificate details based on fields `domain_id` and `resource_fqdn`.



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `domain_id` (String) The ID of the domain to fetch certificates for.
- `resource_fqdn` (String) the fqdn of resource certificate.

### Read-Only

- `certificate` (List of Object) List of certificates retrieved from the API. (see [below for nested schema](#nestedatt--certificate))
- `id` (String) The ID of this resource.

<a id="nestedatt--certificate"></a>
### Nested Schema for `certificate`

Read-Only:

- `certificate_error` (String)
- `domain` (String)
- `expiration_status` (String)
- `is_installed` (Boolean)
- `issued_by` (String)
- `issued_to` (String)
- `key_size` (String)
- `not_after` (String)
- `not_before` (String)
- `number_of_days_to_expire` (Number)
- `pem_encoded` (String)
- `public_key` (String)
- `public_key_algorithm` (String)
- `serial_number` (String)
- `signature_algorithm` (String)
- `subject` (String)
- `subject_alternative_name` (List of String)
- `subject_cn` (String)
- `subject_country` (String)
- `subject_locality` (String)
- `subject_org` (String)
- `subject_ou` (String)
- `subject_st` (String)
- `thumbprint` (String)
- `thumbprint_algorithm` (String)
- `version` (String)
124 changes: 104 additions & 20 deletions internal/certificates/certificate_operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ package certificates

import (
"context"
md52 "crypto/md5"
"encoding/hex"
"fmt"
"io"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
Expand Down Expand Up @@ -68,26 +73,6 @@ func ValidateResourceCertificates(ctx context.Context, client *vcfclient.VcfClie
return nil
}

func GetCertificateForResourceInDomain(ctx context.Context, client *vcfclient.VcfClient,
domainId, resourceFqdn string) (*models.Certificate, error) {
viewCertificatesParams := certificates.NewGetCertificatesByDomainParamsWithContext(ctx).
WithTimeout(constants.DefaultVcfApiCallTimeout)
viewCertificatesParams.ID = domainId

certificatesResponse, _, err := client.Certificates.GetCertificatesByDomain(viewCertificatesParams)
if err != nil {
return nil, err
}

allCertsForDomain := certificatesResponse.Payload.Elements
for _, cert := range allCertsForDomain {
if cert.IssuedTo != nil && *cert.IssuedTo == resourceFqdn {
return cert, nil
}
}
return nil, nil
}

func GenerateCertificateForResource(ctx context.Context, client *api_client.SddcManagerClient,
domainId, resourceType, resourceFqdn, caType *string) error {

Expand Down Expand Up @@ -120,3 +105,102 @@ func GenerateCertificateForResource(ctx context.Context, client *api_client.Sddc
}
return nil
}

func ReadCertificate(ctx context.Context, client *vcfclient.VcfClient,
domainId, resourceFqdn string) (*models.Certificate, error) {
viewCertificatesParams := certificates.NewGetCertificatesByDomainParamsWithContext(ctx).
WithTimeout(constants.DefaultVcfApiCallTimeout)
viewCertificatesParams.ID = domainId

certificatesResponse, _, err := client.Certificates.GetCertificatesByDomain(viewCertificatesParams)
if err != nil {
return nil, fmt.Errorf("failed to get certificate by domain: %w", err)
}

// Check if any certificates are found
if certificatesResponse.Payload == nil || len(certificatesResponse.Payload.Elements) == 0 {
return nil, fmt.Errorf("no certificates found for domain ID %s", domainId)
}

allCertsForDomain := certificatesResponse.Payload.Elements
for _, cert := range allCertsForDomain {
if cert.IssuedTo != nil && *cert.IssuedTo == resourceFqdn {
return cert, nil
}
}
return nil, fmt.Errorf("no certificate found for resource FQDN %s in domain ID %s", resourceFqdn, domainId)
}

func FlattenCertificateWithSubject(cert *models.Certificate) map[string]interface{} {
result := make(map[string]interface{})
if cert.Domain == nil {
result["domain"] = nil
} else {
result["domain"] = *cert.Domain
}
if cert.GetCertificateError == nil {
result["certificate_error"] = nil
} else {
result["certificate_error"] = *cert.GetCertificateError
}

result["expiration_status"] = *cert.ExpirationStatus
result["issued_by"] = *cert.IssuedBy
result["issued_to"] = *cert.IssuedTo
result["key_size"] = *cert.KeySize
result["not_after"] = *cert.NotAfter
result["not_before"] = *cert.NotBefore
result["number_of_days_to_expire"] = *cert.NumberOfDaysToExpire
result["pem_encoded"] = *cert.PemEncoded
result["public_key"] = *cert.PublicKey
result["public_key_algorithm"] = *cert.PublicKeyAlgorithm
result["serial_number"] = *cert.SerialNumber
result["signature_algorithm"] = *cert.SignatureAlgorithm
result["subject"] = *cert.Subject
result["subject_alternative_name"] = cert.SubjectAlternativeName
result["thumbprint"] = *cert.Thumbprint
result["thumbprint_algorithm"] = *cert.ThumbprintAlgorithm
result["version"] = *cert.Version

// Parse the subject string to extract CN, OU, O, L, ST, C
subjectDetails := parseSubject(*cert.Subject)

// Add parsed subject components to the result map
result["subject_cn"] = subjectDetails["CN"]
result["subject_ou"] = subjectDetails["OU"]
result["subject_org"] = subjectDetails["O"]
result["subject_locality"] = subjectDetails["L"]
result["subject_st"] = subjectDetails["ST"]
result["subject_country"] = subjectDetails["C"]

return result
}

func parseSubject(subject string) map[string]string {
parsedSubject := make(map[string]string)

// Split the subject string by commas to separate key-value pairs
subjectParts := strings.Split(subject, ",")

for _, part := range subjectParts {
// Split each part by the equals sign to separate the key and value
keyValue := strings.SplitN(strings.TrimSpace(part), "=", 2)
if len(keyValue) == 2 {
// Store the value in the map with the key as the subject component
parsedSubject[keyValue[0]] = keyValue[1]
}
}

return parsedSubject
}

func HashFields(fields []string) (string, error) {
md5 := md52.New()
_, err := io.WriteString(md5, strings.Join(fields, ""))

if err != nil {
return "", err
}

return hex.EncodeToString(md5.Sum(nil)), nil
}
64 changes: 30 additions & 34 deletions internal/certificates/certificate_subresource.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package certificates

import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/vmware/vcf-sdk-go/models"
)

func CertificateSchema() *schema.Resource {
Expand Down Expand Up @@ -88,6 +87,36 @@ func CertificateSchema() *schema.Resource {
Description: "Complete distinguished name to which the certificate is issued",
Computed: true,
},
"subject_cn": {
Type: schema.TypeString,
Computed: true,
Description: "The subject_cn(common name) of the certificate.",
},
"subject_ou": {
Type: schema.TypeString,
Computed: true,
Description: "The subject_ou(org unit) of the certificate.",
},
"subject_org": {
Type: schema.TypeString,
Computed: true,
Description: "The subject_org of the certificate.",
},
"subject_locality": {
Type: schema.TypeString,
Computed: true,
Description: "The subject_locality of the certificate.",
},
"subject_st": {
Type: schema.TypeString,
Computed: true,
Description: "The subject_st(state) of the certificate.",
},
"subject_country": {
Type: schema.TypeString,
Computed: true,
Description: "The subject_country of the certificate.",
},
"subject_alternative_name": {
Type: schema.TypeList,
Description: "The alternative names to which the certificate is issued",
Expand All @@ -112,36 +141,3 @@ func CertificateSchema() *schema.Resource {
},
}
}

func FlattenCertificate(cert *models.Certificate) map[string]interface{} {
result := make(map[string]interface{})
if cert.Domain == nil {
result["domain"] = "nil"
} else {
result["domain"] = *cert.Domain
}
if cert.GetCertificateError == nil {
result["certificate_error"] = "nil"
} else {
result["certificate_error"] = *cert.GetCertificateError
}

result["expiration_status"] = *cert.ExpirationStatus
result["issued_by"] = *cert.IssuedBy
result["issued_to"] = *cert.IssuedTo
result["key_size"] = *cert.KeySize
result["not_after"] = *cert.NotAfter
result["not_before"] = *cert.NotBefore
result["number_of_days_to_expire"] = *cert.NumberOfDaysToExpire
result["pem_encoded"] = *cert.PemEncoded
result["public_key"] = *cert.PublicKey
result["public_key_algorithm"] = *cert.PublicKeyAlgorithm
result["serial_number"] = *cert.SerialNumber
result["signature_algorithm"] = *cert.SignatureAlgorithm
result["subject"] = *cert.Subject
result["subject_alternative_name"] = cert.SubjectAlternativeName
result["thumbprint"] = *cert.Thumbprint
result["thumbprint_algorithm"] = *cert.ThumbprintAlgorithm
result["version"] = *cert.Version
return result
}
Loading

0 comments on commit fb8d578

Please sign in to comment.