Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for new mTLS certificate store API #1150

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/1150.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
mtls_certificate: add support for managing mTLS certificates and assocations
```
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ The current feature list includes:
- [x] [Load Balancing](https://blog.cloudflare.com/introducing-load-balancing-intelligent-failover-with-cloudflare/)
- [x] [Logpush Jobs](https://developers.cloudflare.com/logs/logpush/)
- [x] Magic Transit / Magic WAN
- [x] mTLS Certificate Store
- [x] Notifications
- [ ] Organization Administration
- [x] [Origin CA](https://blog.cloudflare.com/universal-ssl-encryption-all-the-way-to-the-origin-for-free/)
Expand Down
214 changes: 214 additions & 0 deletions mtls_certificates.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package cloudflare

import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
)

// MTLSAssociation represents the metadata for an existing association
// between a user-uploaded mTLS certificate and a Cloudflare service.
type MTLSAssociation struct {
Service string `json:"service"`
Status string `json:"status"`
}

// MTLSAssociationResponse represents the response from the retrieval endpoint
// for mTLS certificate associations.
type MTLSAssociationResponse struct {
Response
Result []MTLSAssociation `json:"result"`
}

// MTLSCertificate represents the metadata for a user-uploaded mTLS
// certificate.
type MTLSCertificate struct {
ID string `json:"id"`
Name string `json:"name"`
Issuer string `json:"issuer"`
Signature string `json:"signature"`
SerialNumber string `json:"serial_number"`
Certificates string `json:"certificates"`
CA bool `json:"ca"`
UploadedOn time.Time `json:"uploaded_on"`
UpdatedAt time.Time `json:"updated_at"`
ExpiresOn time.Time `json:"expires_on"`
}

// MTLSCertificateResponse represents the response from endpoints relating to
// retrieving, creating, and deleting an mTLS certificate.
type MTLSCertificateResponse struct {
Response
Result MTLSCertificate `json:"result"`
}

// MTLSCertificatesResponse represents the response from the mTLS certificate
// list endpoint.
type MTLSCertificatesResponse struct {
Response
Result []MTLSCertificate `json:"result"`
ResultInfo `json:"result_info"`
}

// MTLSCertificateParams represents the data related to the mTLS certificate
// being uploaded. Name is an optional field.
type CreateMTLSCertificateParams struct {
Name string `json:"name"`
Certificates string `json:"certificates"`
PrivateKey string `json:"private_key"`
CA bool `json:"ca"`
}

type ListMTLSCertificatesParams struct {
PaginationOptions
Limit int `url:"limit,omitempty"`
Offset int `url:"offset,omitempty"`
Name string `url:"name,omitempty"`
CA bool `url:"ca,omitempty"`
}

type ListMTLSCertificateAssociationsParams struct {
CertificateID string
}

var (
ErrMissingCertificateID = errors.New("missing required certificate ID")
)

// ListMTLSCertificates returns a list of all user-uploaded mTLS certificates.
//
// API reference: https://api.cloudflare.com/#mtls-certificate-management-list-mtls-certificates
func (api *API) ListMTLSCertificates(ctx context.Context, rc *ResourceContainer, params ListMTLSCertificatesParams) ([]MTLSCertificate, ResultInfo, error) {
if rc.Level != AccountRouteLevel {
return []MTLSCertificate{}, ResultInfo{}, ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return []MTLSCertificate{}, ResultInfo{}, ErrMissingAccountID
}

uri := fmt.Sprintf("/accounts/%s/mtls_certificates", rc.Identifier)
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, params)
if err != nil {
return []MTLSCertificate{}, ResultInfo{}, err
}
var r MTLSCertificatesResponse
if err := json.Unmarshal(res, &r); err != nil {
return []MTLSCertificate{}, ResultInfo{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, r.ResultInfo, err
}

// GetMTLSCertificate returns the metadata associated with a user-uploaded mTLS
// certificate.
//
// API reference: https://api.cloudflare.com/#mtls-certificate-management-get-mtls-certificate
func (api *API) GetMTLSCertificate(ctx context.Context, rc *ResourceContainer, certificateID string) (MTLSCertificate, error) {
if rc.Level != AccountRouteLevel {
return MTLSCertificate{}, ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return MTLSCertificate{}, ErrMissingAccountID
}

if certificateID == "" {
return MTLSCertificate{}, ErrMissingCertificateID
}

uri := fmt.Sprintf("/accounts/%s/mtls_certificates/%s", rc.Identifier, certificateID)
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return MTLSCertificate{}, err
}
var r MTLSCertificateResponse
if err := json.Unmarshal(res, &r); err != nil {
return MTLSCertificate{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}

// ListMTLSCertificateAssociations returns a list of all existing associations
// between the mTLS certificate and Cloudflare services.
//
// API reference: https://api.cloudflare.com/#mtls-certificate-management-list-mtls-certificate-associations
func (api *API) ListMTLSCertificateAssociations(ctx context.Context, rc *ResourceContainer, params ListMTLSCertificateAssociationsParams) ([]MTLSAssociation, error) {
if rc.Level != AccountRouteLevel {
return []MTLSAssociation{}, ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return []MTLSAssociation{}, ErrMissingAccountID
}

if params.CertificateID == "" {
return []MTLSAssociation{}, ErrMissingCertificateID
}

uri := fmt.Sprintf("/accounts/%s/mtls_certificates/%s/associations", rc.Identifier, params.CertificateID)
res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return []MTLSAssociation{}, err
}
var r MTLSAssociationResponse
if err := json.Unmarshal(res, &r); err != nil {
return []MTLSAssociation{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}

// CreateMTLSCertificate will create the provided certificate for use with mTLS
// enabled Cloudflare services.
//
// API reference: https://api.cloudflare.com/#mtls-certificate-management-upload-mtls-certificate
func (api *API) CreateMTLSCertificate(ctx context.Context, rc *ResourceContainer, params CreateMTLSCertificateParams) (MTLSCertificate, error) {
if rc.Level != AccountRouteLevel {
return MTLSCertificate{}, ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return MTLSCertificate{}, ErrMissingAccountID
}

uri := fmt.Sprintf("/accounts/%s/mtls_certificates", rc.Identifier)
res, err := api.makeRequestContext(ctx, http.MethodPost, uri, params)
if err != nil {
return MTLSCertificate{}, err
}
var r MTLSCertificateResponse
if err := json.Unmarshal(res, &r); err != nil {
return MTLSCertificate{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}

// DeleteMTLSCertificate will delete the specified mTLS certificate.
//
// API reference: https://api.cloudflare.com/#mtls-certificate-management-delete-mtls-certificate
func (api *API) DeleteMTLSCertificate(ctx context.Context, rc *ResourceContainer, certificateID string) (MTLSCertificate, error) {
if rc.Level != AccountRouteLevel {
return MTLSCertificate{}, ErrRequiredAccountLevelResourceContainer
}

if rc.Identifier == "" {
return MTLSCertificate{}, ErrMissingAccountID
}

if certificateID == "" {
return MTLSCertificate{}, ErrMissingCertificateID
}

uri := fmt.Sprintf("/accounts/%s/mtls_certificates/%s", rc.Identifier, certificateID)
res, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)
if err != nil {
return MTLSCertificate{}, err
}
var r MTLSCertificateResponse
if err := json.Unmarshal(res, &r); err != nil {
return MTLSCertificate{}, fmt.Errorf("%s: %w", errUnmarshalError, err)
}
return r.Result, nil
}
Loading