From ff2c0b2f027b96c45927940a2d2af83de6f5543c Mon Sep 17 00:00:00 2001 From: Joel Guerra Date: Wed, 9 Dec 2020 03:00:14 -0800 Subject: [PATCH] Custom TLS API (#225) * adds custom cert types and can GET list certs * can get a single custom certificate * adds delete, updated, create custom TLS * cleaned up for feedback * get, list and patch custom tls configurations * adds test fixtures * adds TLSActivation API operations * go fmt * adds tls configuration tests and fixtures * adds tests for activation, fixes relationships * fixes server error due to API not accepting "type" to be blank * adds "Type" tag to update activation * updated comments, fmt * update errors * adds Type field to input structs to get around API bug * updates Create and Update input struct tags per feedback * adds comments to exported functions * Update fastly/custom_tls_activation.go Co-authored-by: Patrick Hamann * Update fastly/custom_tls_activation.go Co-authored-by: Patrick Hamann * Update fastly/custom_tls_activation.go "Include" value Co-authored-by: Patrick Hamann * Update fastly/custom_tls_configuration.go Co-authored-by: Patrick Hamann * Update fastly/custom_tls_activation.go Co-authored-by: Patrick Hamann * Update fastly/custom_tls_activation.go Co-authored-by: Patrick Hamann * Update fastly/custom_tls_activation.go Co-authored-by: Patrick Hamann * Update fastly/custom_tls_certificate.go Co-authored-by: Patrick Hamann * adds include param to GetTLSActivation() * adds tls protocols to CustomTLSConfiguration * adds include to ListCustomTLSConfigurationsInput * removes white space and compares i.Include to nil rather than an empty string * updates tls activation tests to match changes to tls activation child types * Refactors CustomCertificate to CustomTLSCertificate and updates all related tests and CRUD functions * removes redudant struct tag, adds tests to confirm child struct still deserializes * removes redudant "TLS" in Activation input types, updates tests to check for matching IDs in get and list * adds robust input unit testing for TLS activation operations * adds input validation tests for custom TLS certificate operations * adds input validation unit tests to TLS configuration operations * adds missing params to ListCustomCertificates * Update fastly/custom_tls_activation.go Co-authored-by: Patrick Hamann * Update fastly/custom_tls_certificate.go Co-authored-by: Patrick Hamann * Update fastly/custom_tls_activation.go Co-authored-by: Patrick Hamann * Update fastly/custom_tls_activation.go Co-authored-by: Patrick Hamann * remove pesky type * Update fastly/custom_tls_configuration.go Co-authored-by: Mark McDonnell * Update fastly/custom_tls_configuration.go Co-authored-by: Mark McDonnell * Update fastly/errors.go Co-authored-by: Mark McDonnell * Update fastly/errors.go Co-authored-by: Mark McDonnell * small changes to match convention, per feedback * converts list operations to not use pointers in their input structs * Update fastly/errors.go Co-authored-by: Mark McDonnell * Update fastly/errors.go Co-authored-by: Mark McDonnell * Update fastly/errors.go Co-authored-by: Mark McDonnell * fixes ListCustomTLSCertificates filter * go fmt * Update fastly/custom_tls_certificate.go Co-authored-by: Mark McDonnell * Update fastly/errors.go Co-authored-by: Mark McDonnell * Update fastly/errors.go Co-authored-by: Mark McDonnell * Update fastly/errors.go Co-authored-by: Mark McDonnell * pushing filter fix where GH "commit suggestion" didnt work Co-authored-by: Patrick Hamann Co-authored-by: Mark McDonnell --- fastly/custom_tls_activation.go | 205 ++++++++++++++++ fastly/custom_tls_activation_test.go | 222 ++++++++++++++++++ fastly/custom_tls_certificate.go | 205 ++++++++++++++++ fastly/custom_tls_certificate_test.go | 211 +++++++++++++++++ fastly/custom_tls_configuration.go | 163 +++++++++++++ fastly/custom_tls_configuration_test.go | 118 ++++++++++ fastly/errors.go | 12 + fastly/fixtures/custom_tls/cleanup.yaml | 37 +++ fastly/fixtures/custom_tls/create.yaml | 50 ++++ fastly/fixtures/custom_tls/delete.yaml | 37 +++ fastly/fixtures/custom_tls/get.yaml | 43 ++++ fastly/fixtures/custom_tls/list.yaml | 45 ++++ fastly/fixtures/custom_tls/update.yaml | 46 ++++ .../custom_tls_activation/cleanup.yaml | 43 ++++ .../custom_tls_activation/create.yaml | 48 ++++ .../custom_tls_activation/delete.yaml | 43 ++++ .../fixtures/custom_tls_activation/get.yaml | 43 ++++ .../fixtures/custom_tls_activation/list.yaml | 45 ++++ .../custom_tls_activation/update.yaml | 48 ++++ .../custom_tls_configuration/get.yaml | 43 ++++ .../custom_tls_configuration/list.yaml | 46 ++++ .../custom_tls_configuration/update.yaml | 46 ++++ 22 files changed, 1799 insertions(+) create mode 100644 fastly/custom_tls_activation.go create mode 100644 fastly/custom_tls_activation_test.go create mode 100644 fastly/custom_tls_certificate.go create mode 100644 fastly/custom_tls_certificate_test.go create mode 100644 fastly/custom_tls_configuration.go create mode 100644 fastly/custom_tls_configuration_test.go create mode 100644 fastly/fixtures/custom_tls/cleanup.yaml create mode 100644 fastly/fixtures/custom_tls/create.yaml create mode 100644 fastly/fixtures/custom_tls/delete.yaml create mode 100644 fastly/fixtures/custom_tls/get.yaml create mode 100644 fastly/fixtures/custom_tls/list.yaml create mode 100644 fastly/fixtures/custom_tls/update.yaml create mode 100644 fastly/fixtures/custom_tls_activation/cleanup.yaml create mode 100644 fastly/fixtures/custom_tls_activation/create.yaml create mode 100644 fastly/fixtures/custom_tls_activation/delete.yaml create mode 100644 fastly/fixtures/custom_tls_activation/get.yaml create mode 100644 fastly/fixtures/custom_tls_activation/list.yaml create mode 100644 fastly/fixtures/custom_tls_activation/update.yaml create mode 100644 fastly/fixtures/custom_tls_configuration/get.yaml create mode 100644 fastly/fixtures/custom_tls_configuration/list.yaml create mode 100644 fastly/fixtures/custom_tls_configuration/update.yaml diff --git a/fastly/custom_tls_activation.go b/fastly/custom_tls_activation.go new file mode 100644 index 000000000..19b3dad2d --- /dev/null +++ b/fastly/custom_tls_activation.go @@ -0,0 +1,205 @@ +package fastly + +import ( + "fmt" + "reflect" + "strconv" + "time" + + "github.com/google/jsonapi" +) + +// TLSActivation represents a /tls/activations response. +type TLSActivation struct { + ID string `jsonapi:"primary,tls_activation"` + Configuration *TLSConfiguration `jsonapi:"relation,tls_configuration"` // TLSConfiguration type shared with BulkCertificate + Domain *TLSDomain `jsonapi:"relation,tls_domain"` // TLSDomain type shared with BulkCertificate + Certificate *CustomTLSCertificate `jsonapi:"relation,tls_certificate"` + CreatedAt *time.Time `jsonapi:"attr,created_at,iso8601"` +} + +// ListTLSActivationsInput is used as input to the ListTLSActivations function. +type ListTLSActivationsInput struct { + FilterTLSCertificateID string // Limit the returned activations to a specific certificate. + FilterTLSConfigurationID string // Limit the returned activations to a specific TLS configuration. + FilterTLSDomainID string // Limit the returned rules to a specific domain name. + Include string // Include related objects. Optional, comma-separated values. Permitted values: tls_certificate, tls_configuration, and tls_domain. + PageNumber int // The page index for pagination. + PageSize int // The number of activations per page. +} + +// formatFilters converts user input into query parameters for filtering. +func (i *ListTLSActivationsInput) formatFilters() map[string]string { + result := map[string]string{} + pairings := map[string]interface{}{ + "filter[tls_certificate.id]": i.FilterTLSCertificateID, + "filter[tls_configuration.id]": i.FilterTLSConfigurationID, + "filter[tls_domain.id]": i.FilterTLSDomainID, + "include": i.Include, + "page[number]": i.PageNumber, + "page[size]": i.PageSize, + } + + for key, value := range pairings { + switch t := reflect.TypeOf(value).String(); t { + case "string": + if value != "" { + result[key] = value.(string) + } + case "int": + if value != 0 { + result[key] = strconv.Itoa(value.(int)) + } + } + } + + return result +} + +// ListTLSActivations list all activations. +func (c *Client) ListTLSActivations(i *ListTLSActivationsInput) ([]*TLSActivation, error) { + p := "/tls/activations" + filters := &RequestOptions{ + Params: i.formatFilters(), + Headers: map[string]string{ + "Accept": "application/vnd.api+json", // this is required otherwise the filters don't work + }, + } + + r, err := c.Get(p, filters) + if err != nil { + return nil, err + } + + data, err := jsonapi.UnmarshalManyPayload(r.Body, reflect.TypeOf(new(TLSActivation))) + if err != nil { + return nil, err + } + + a := make([]*TLSActivation, len(data)) + for i := range data { + typed, ok := data[i].(*TLSActivation) + if !ok { + return nil, fmt.Errorf("unexpected response type: %T", data[i]) + } + a[i] = typed + } + + return a, nil +} + +// GetTLSActivationInput is used as input to the GetTLSActivation function. +type GetTLSActivationInput struct { + ID string + Include *string // Include related objects. Optional, comma-separated values. Permitted values: tls_certificate, tls_configuration, and tls_domain. +} + +// GetTLSActivation retrieve a single activation. +func (c *Client) GetTLSActivation(i *GetTLSActivationInput) (*TLSActivation, error) { + if i.ID == "" { + return nil, ErrMissingID + } + + p := fmt.Sprintf("/tls/activations/%s", i.ID) + + ro := &RequestOptions{ + Headers: map[string]string{ + "Accept": "application/vnd.api+json", // this is required otherwise the params don't work + }, + } + + if i.Include != nil { + ro.Params = map[string]string{"include": *i.Include} + } + + r, err := c.Get(p, ro) + if err != nil { + return nil, err + } + + var a TLSActivation + if err := jsonapi.UnmarshalPayload(r.Body, &a); err != nil { + return nil, err + } + + return &a, nil +} + +// CreateTLSActivationInput is used as input to the CreateTLSActivation function. +type CreateTLSActivationInput struct { + ID string `jsonapi:"primary,tls_activation"` // ID value does not need to be set. + Certificate *CustomTLSCertificate `jsonapi:"relation,tls_certificate"` // Only ID of CustomTLSCertificate needs to be set. + Configuration *TLSConfiguration `jsonapi:"relation,tls_configuration"` + Domain *TLSDomain `jsonapi:"relation,tls_domain"` +} + +// CreateTLSActivation enable TLS for a domain using a custom certificate. +func (c *Client) CreateTLSActivation(i *CreateTLSActivationInput) (*TLSActivation, error) { + if i.Certificate == nil { + return nil, ErrMissingTLSCertificate + } + if i.Configuration == nil { + return nil, ErrMissingTLSConfiguration + } + if i.Domain == nil { + return nil, ErrMissingTLSDomain + } + + p := "/tls/activations" + + r, err := c.PostJSONAPI(p, i, nil) + if err != nil { + return nil, err + } + + var a TLSActivation + if err := jsonapi.UnmarshalPayload(r.Body, &a); err != nil { + return nil, err + } + + return &a, nil +} + +// UpdateTLSActivationInput is used as input to the UpdateTLSActivation function. +type UpdateTLSActivationInput struct { + ID string `jsonapi:"primary,tls_activation"` + Certificate *CustomTLSCertificate `jsonapi:"relation,tls_certificate"` // Only ID of CustomTLSCertificate needs to be set. +} + +// UpdateTLSActivation updates the certificate used to terminate TLS traffic for the domain associated with this TLS activation. +func (c *Client) UpdateTLSActivation(i *UpdateTLSActivationInput) (*TLSActivation, error) { + if i.ID == "" { + return nil, ErrMissingID + } + if i.Certificate == nil { + return nil, ErrMissingTLSCertificate + } + + path := fmt.Sprintf("/tls/activations/%s", i.ID) + resp, err := c.PatchJSONAPI(path, i, nil) + if err != nil { + return nil, err + } + + var ta TLSActivation + if err := jsonapi.UnmarshalPayload(resp.Body, &ta); err != nil { + return nil, err + } + return &ta, nil +} + +// DeleteTLSActivationInput used for deleting a certificate. +type DeleteTLSActivationInput struct { + ID string +} + +// DeleteTLSActivation destroy a certificate. +func (c *Client) DeleteTLSActivation(i *DeleteTLSActivationInput) error { + if i.ID == "" { + return ErrMissingID + } + + path := fmt.Sprintf("/tls/activations/%s", i.ID) + _, err := c.Delete(path, nil) + return err +} diff --git a/fastly/custom_tls_activation_test.go b/fastly/custom_tls_activation_test.go new file mode 100644 index 000000000..e399485cb --- /dev/null +++ b/fastly/custom_tls_activation_test.go @@ -0,0 +1,222 @@ +package fastly + +import ( + "testing" +) + +func TestClient_TLSActivation(t *testing.T) { + t.Parallel() + + fixtureBase := "custom_tls_activation/" + + // Create + var err error + var ta *TLSActivation + record(t, fixtureBase+"create", func(c *Client) { + ta, err = c.CreateTLSActivation(&CreateTLSActivationInput{ + Certificate: &CustomTLSCertificate{ID: "CERTIFICATE_ID"}, + Configuration: &TLSConfiguration{ID: "CONFIGURATION_ID"}, + Domain: &TLSDomain{ID: "DOMAIN_NAME"}, + }) + }) + if err != nil { + t.Fatal(err) + } + + // Ensure deleted + defer func() { + record(t, fixtureBase+"cleanup", func(c *Client) { + c.DeleteTLSActivation(&DeleteTLSActivationInput{ + ID: ta.ID, + }) + }) + }() + + // List + var lta []*TLSActivation + record(t, fixtureBase+"list", func(c *Client) { + lta, err = c.ListTLSActivations(&ListTLSActivationsInput{}) + }) + if err != nil { + t.Fatal(err) + } + if len(lta) < 1 { + t.Errorf("bad TLS activations: %v", lta) + } + if lta[0].Certificate == nil { + t.Errorf("TLS certificate relation should not be nil: %v", lta) + } + if lta[0].Certificate.ID != ta.Certificate.ID { + t.Errorf("bad Certificate ID: %q (%q)", lta[0].Certificate.ID, ta.Certificate.ID) + } + if lta[0].Configuration == nil { + t.Errorf("TLS Configuration relation should not be nil: %v", lta) + } + if lta[0].Configuration.ID != ta.Configuration.ID { + t.Errorf("bad Configuration ID: %q (%q)", lta[0].Configuration.ID, ta.Configuration.ID) + } + if lta[0].Domain == nil { + t.Errorf("TLS domain relation should not be nil: %v", lta) + } + if lta[0].Domain.ID != ta.Domain.ID { + t.Errorf("bad Domain ID: %q (%q)", lta[0].Domain.ID, ta.Domain.ID) + } + + // Get + var gta *TLSActivation + record(t, fixtureBase+"get", func(c *Client) { + gta, err = c.GetTLSActivation(&GetTLSActivationInput{ + ID: ta.ID, + }) + }) + if err != nil { + t.Fatal(err) + } + if ta.ID != gta.ID { + t.Errorf("bad ID: %q (%q)", ta.ID, gta.ID) + } + + // Update + var uta *TLSActivation + record(t, fixtureBase+"update", func(c *Client) { + uta, err = c.UpdateTLSActivation(&UpdateTLSActivationInput{ + ID: "ACTIVATION_ID", + Certificate: &CustomTLSCertificate{}, + }) + }) + if err != nil { + t.Fatal(err) + } + if ta.ID != uta.ID { + t.Errorf("bad ID: %q (%q)", ta.ID, uta.ID) + } + + // Delete + record(t, fixtureBase+"delete", func(c *Client) { + err = c.DeleteTLSActivation(&DeleteTLSActivationInput{ + ID: ta.ID, + }) + }) + if err != nil { + t.Fatal(err) + } +} + +func TestClient_CreateTLSActivation_validation(t *testing.T) { + t.Parallel() + + var err error + record(t, "custom_tls_activation/create", func(c *Client) { + _, err = c.CreateTLSActivation(&CreateTLSActivationInput{ + Certificate: &CustomTLSCertificate{ID: "CERTIFICATE_ID"}, + Configuration: &TLSConfiguration{ID: "CONFIGURATION_ID"}, + Domain: &TLSDomain{ID: "DOMAIN_NAME"}, + }) + }) + if err != nil { + t.Fatal(err) + } + + _, err = testClient.CreateTLSActivation(&CreateTLSActivationInput{ + Configuration: &TLSConfiguration{ID: "CONFIGURATION_ID"}, + Domain: &TLSDomain{ID: "DOMAIN_NAME"}, + }) + if err != ErrMissingTLSCertificate { + t.Errorf("bad error: %s", err) + } + + _, err = testClient.CreateTLSActivation(&CreateTLSActivationInput{ + Certificate: &CustomTLSCertificate{ID: "CERTIFICATE_ID"}, + Domain: &TLSDomain{ID: "DOMAIN_NAME"}, + }) + if err != ErrMissingTLSConfiguration { + t.Errorf("bad error: %s", err) + } + + _, err = testClient.CreateTLSActivation(&CreateTLSActivationInput{ + Certificate: &CustomTLSCertificate{ID: "CERTIFICATE_ID"}, + Configuration: &TLSConfiguration{ID: "CONFIGURATION_ID"}, + }) + if err != ErrMissingTLSDomain { + t.Errorf("bad error: %s", err) + } +} + +func TestClient_DeleteTLSActivation_validation(t *testing.T) { + t.Parallel() + + var err error + record(t, "custom_tls_activation/delete", func(c *Client) { + err = c.DeleteTLSActivation(&DeleteTLSActivationInput{ + ID: "ACTIVATION_ID", + }) + }) + if err != nil { + t.Fatal(err) + } + + err = testClient.DeleteTLSActivation(&DeleteTLSActivationInput{}) + if err != ErrMissingID { + t.Errorf("bad error: %s", err) + } +} + +func TestClient_ListTLSActivations_validation(t *testing.T) { + t.Parallel() + + var err error + record(t, "custom_tls_activation/list", func(c *Client) { + _, err = c.ListTLSActivations(&ListTLSActivationsInput{}) + }) + if err != nil { + t.Fatal(err) + } +} + +func TestClient_GetTLSActivation_validation(t *testing.T) { + t.Parallel() + + var err error + record(t, "custom_tls_activation/get", func(c *Client) { + _, err = c.GetTLSActivation(&GetTLSActivationInput{ + ID: "ACTIVATION_ID", + }) + }) + if err != nil { + t.Fatal(err) + } + + _, err = testClient.GetTLSActivation(&GetTLSActivationInput{}) + if err != ErrMissingID { + t.Errorf("bad error: %s", err) + } +} + +func TestClient_UpdateTLSActivation_validation(t *testing.T) { + t.Parallel() + + var err error + record(t, "custom_tls_activation/update", func(c *Client) { + _, err = c.UpdateTLSActivation(&UpdateTLSActivationInput{ + ID: "ACTIVATION_ID", + Certificate: &CustomTLSCertificate{ID: "CERTIFICATE_ID"}, + }) + }) + if err != nil { + t.Fatal(err) + } + + _, err = testClient.UpdateTLSActivation(&UpdateTLSActivationInput{ + ID: "ACTIVATION_ID", + }) + if err != ErrMissingTLSCertificate { + t.Errorf("bad error: %s", err) + } + + _, err = testClient.UpdateTLSActivation(&UpdateTLSActivationInput{ + Certificate: &CustomTLSCertificate{ID: "CERTIFICATE_ID"}, + }) + if err != ErrMissingID { + t.Errorf("bad error: %s", err) + } +} diff --git a/fastly/custom_tls_certificate.go b/fastly/custom_tls_certificate.go new file mode 100644 index 000000000..10d259e88 --- /dev/null +++ b/fastly/custom_tls_certificate.go @@ -0,0 +1,205 @@ +package fastly + +import ( + "fmt" + "reflect" + "strconv" + "time" + + "github.com/google/jsonapi" +) + +// CustomTLSCertificate represents a custom certificate. Uses common TLSDomain type from BulkCertificate. +type CustomTLSCertificate struct { + ID string `jsonapi:"primary,tls_certificate"` + IssuedTo string `jsonapi:"attr,issued_to"` + Issuer string `jsonapi:"attr,issuer"` + Name string `jsonapi:"attr,name"` + NotAfter *time.Time `jsonapi:"attr,not_after,iso8601"` + NotBefore *time.Time `jsonapi:"attr,not_before,iso8601"` + Replace bool `jsonapi:"attr,replace"` + SerialNumber string `jsonapi:"attr,serial_number"` + SignatureAlgorithm string `jsonapi:"attr,signature_algorithm"` + TLSDomains []*TLSDomain `jsonapi:"relation,tls_domains"` + CreatedAt *time.Time `jsonapi:"attr,created_at,iso8601"` + UpdatedAt *time.Time `jsonapi:"attr,updated_at,iso8601"` +} + +// ListCustomTLSCertificatesInput is used as input to the ListCustomTLSCertificatesInput function. +type ListCustomTLSCertificatesInput struct { + FilterNotAfter string // Limit the returned certificates to those that expire prior to the specified date in UTC. Accepts parameters: lte (e.g., filter[not_after][lte]=2020-05-05). + FilterTLSDomainsID string // Limit the returned certificates to those that include the specific domain. + Include string // Include related objects. Optional, comma-separated values. Permitted values: tls_activations. + PageNumber int // The page index for pagination. + PageSize int // The number of keys per page. + Sort string // The order in which to list certificates. Valid values are created_at, not_before, not_after. May precede any value with a - for descending. +} + +// formatFilters converts user input into query parameters for filtering. +func (i *ListCustomTLSCertificatesInput) formatFilters() map[string]string { + result := map[string]string{} + pairings := map[string]interface{}{ + "filter[not_after]": i.FilterNotAfter, + "filter[tls_domains.id]": i.FilterTLSDomainsID, + "include": i.Include, + "page[size]": i.PageSize, + "page[number]": i.PageNumber, + "sort": i.Sort, + } + + for key, value := range pairings { + switch t := reflect.TypeOf(value).String(); t { + case "string": + if value != "" { + result[key] = value.(string) + } + case "int": + if value != 0 { + result[key] = strconv.Itoa(value.(int)) + } + } + } + + return result +} + +// ListCustomTLSCertificates list all certificates. +func (c *Client) ListCustomTLSCertificates(i *ListCustomTLSCertificatesInput) ([]*CustomTLSCertificate, error) { + p := "/tls/certificates" + filters := &RequestOptions{ + Params: i.formatFilters(), + Headers: map[string]string{ + "Accept": "application/vnd.api+json", // this is required otherwise the filters don't work + }, + } + + r, err := c.Get(p, filters) + if err != nil { + return nil, err + } + + data, err := jsonapi.UnmarshalManyPayload(r.Body, reflect.TypeOf(new(CustomTLSCertificate))) + if err != nil { + return nil, err + } + + cc := make([]*CustomTLSCertificate, len(data)) + for i := range data { + typed, ok := data[i].(*CustomTLSCertificate) + if !ok { + return nil, fmt.Errorf("unexpected response type: %T", data[i]) + } + cc[i] = typed + } + + return cc, nil +} + +// GetCustomTLSCertificateInput is used as input to the GetCustomTLSCertificate function. +type GetCustomTLSCertificateInput struct { + ID string +} + +func (c *Client) GetCustomTLSCertificate(i *GetCustomTLSCertificateInput) (*CustomTLSCertificate, error) { + if i.ID == "" { + return nil, ErrMissingID + } + + p := fmt.Sprintf("/tls/certificates/%s", i.ID) + + r, err := c.Get(p, nil) + if err != nil { + return nil, err + } + + var cc CustomTLSCertificate + if err := jsonapi.UnmarshalPayload(r.Body, &cc); err != nil { + return nil, err + } + + return &cc, nil +} + +// CreateCustomTLSCertificateInput is used as input to the CreateCustomTLSCertificate function. +type CreateCustomTLSCertificateInput struct { + ID string `jsonapi:"primary,tls_certificate"` // ID value does not need to be set. + CertBlob string `jsonapi:"attr,cert_blob"` + Name string `jsonapi:"attr,name"` +} + +// CreateCustomTLSCertificate creates a custom TLS certificate. +func (c *Client) CreateCustomTLSCertificate(i *CreateCustomTLSCertificateInput) (*CustomTLSCertificate, error) { + if i.CertBlob == "" { + return nil, ErrMissingCertBlob + } + if i.Name == "" { + return nil, ErrMissingName + } + + p := "/tls/certificates" + + r, err := c.PostJSONAPI(p, i, nil) + if err != nil { + return nil, err + } + + var cc CustomTLSCertificate + if err := jsonapi.UnmarshalPayload(r.Body, &cc); err != nil { + return nil, err + } + + return &cc, nil +} + +// UpdateCustomTLSCertificateInput is used as input to the UpdateCustomTLSCertificate function. +type UpdateCustomTLSCertificateInput struct { + ID string `jsonapi:"primary,tls_certificate"` + CertBlob string `jsonapi:"attr,cert_blob"` + Name string `jsonapi:"attr,name"` +} + +// UpdateCustomTLSCertificate replace a certificate with a newly reissued certificate. +// By using this endpoint, the original certificate will cease to be used for future TLS handshakes. +// Thus, only SAN entries that appear in the replacement certificate will become TLS enabled. +// Any SAN entries that are missing in the replacement certificate will become disabled. +func (c *Client) UpdateCustomTLSCertificate(i *UpdateCustomTLSCertificateInput) (*CustomTLSCertificate, error) { + if i.ID == "" { + return nil, ErrMissingID + } + + if i.CertBlob == "" { + return nil, ErrMissingCertBlob + } + + if i.Name == "" { + return nil, ErrMissingName + } + + path := fmt.Sprintf("/tls/certificates/%s", i.ID) + resp, err := c.PatchJSONAPI(path, i, nil) + if err != nil { + return nil, err + } + + var cc CustomTLSCertificate + if err := jsonapi.UnmarshalPayload(resp.Body, &cc); err != nil { + return nil, err + } + return &cc, nil +} + +// DeleteCustomTLSCertificateInput used for deleting a certificate. +type DeleteCustomTLSCertificateInput struct { + ID string +} + +// DeleteCustomTLSCertificate destroy a certificate. This disables TLS for all domains listed as SAN entries. +func (c *Client) DeleteCustomTLSCertificate(i *DeleteCustomTLSCertificateInput) error { + if i.ID == "" { + return ErrMissingID + } + + path := fmt.Sprintf("/tls/certificates/%s", i.ID) + _, err := c.Delete(path, nil) + return err +} diff --git a/fastly/custom_tls_certificate_test.go b/fastly/custom_tls_certificate_test.go new file mode 100644 index 000000000..7d1b1eb33 --- /dev/null +++ b/fastly/custom_tls_certificate_test.go @@ -0,0 +1,211 @@ +package fastly + +import "testing" + +func TestClient_CustomTLSCertificate(t *testing.T) { + t.Parallel() + + fixtureBase := "custom_tls/" + + // Create + var err error + var cc *CustomTLSCertificate + record(t, fixtureBase+"create", func(c *Client) { + cc, err = c.CreateCustomTLSCertificate(&CreateCustomTLSCertificateInput{ + CertBlob: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n", + Name: "My certificate", + }) + }) + if err != nil { + t.Fatal(err) + } + + // Ensure deleted + defer func() { + record(t, fixtureBase+"cleanup", func(c *Client) { + c.DeleteCustomTLSCertificate(&DeleteCustomTLSCertificateInput{ + ID: cc.ID, + }) + }) + }() + + // List + var lcc []*CustomTLSCertificate + record(t, fixtureBase+"list", func(c *Client) { + lcc, err = c.ListCustomTLSCertificates(&ListCustomTLSCertificatesInput{}) + }) + if err != nil { + t.Fatal(err) + } + if len(lcc) < 1 { + t.Errorf("bad Custom certificates: %v", lcc) + } + + // Get + var gcc *CustomTLSCertificate + record(t, fixtureBase+"get", func(c *Client) { + gcc, err = c.GetCustomTLSCertificate(&GetCustomTLSCertificateInput{ + ID: cc.ID, + }) + }) + if err != nil { + t.Fatal(err) + } + if cc.ID != gcc.ID { + t.Errorf("bad ID: %q (%q)", cc.ID, gcc.ID) + } + if gcc.TLSDomains == nil { + t.Errorf("TLSDomains should not be nil: %v", cc.TLSDomains) + } + if len(gcc.TLSDomains) < 1 { + t.Errorf("TLSDomains should not be an empty slice: %v", cc.TLSDomains) + } + if cc.TLSDomains[0].ID != gcc.TLSDomains[0].ID { + t.Errorf("bad Domain ID: %q (%q)", cc.TLSDomains[0].ID, gcc.TLSDomains[0].ID) + } + + // Update + var ucc *CustomTLSCertificate + record(t, fixtureBase+"update", func(c *Client) { + ucc, err = c.UpdateCustomTLSCertificate(&UpdateCustomTLSCertificateInput{ + ID: "CERTIFICATE_ID", + CertBlob: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n", + Name: "My certificate", + }) + }) + if err != nil { + t.Fatal(err) + } + if cc.ID != ucc.ID { + t.Errorf("bad ID: %q (%q)", cc.ID, ucc.ID) + } + + // Delete + record(t, fixtureBase+"delete", func(c *Client) { + err = c.DeleteCustomTLSCertificate(&DeleteCustomTLSCertificateInput{ + ID: cc.ID, + }) + }) + if err != nil { + t.Fatal(err) + } +} + +func TestClient_CreateCustomTLSCertificate_validation(t *testing.T) { + t.Parallel() + + var err error + record(t, "custom_tls/create", func(c *Client) { + _, err = c.CreateCustomTLSCertificate(&CreateCustomTLSCertificateInput{ + CertBlob: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n", + Name: "My certificate", + }) + }) + if err != nil { + t.Fatal(err) + } + + _, err = testClient.CreateCustomTLSCertificate(&CreateCustomTLSCertificateInput{ + CertBlob: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n", + }) + if err != ErrMissingName { + t.Errorf("bad error: %s", err) + } + + _, err = testClient.CreateCustomTLSCertificate(&CreateCustomTLSCertificateInput{ + Name: "My certificate", + }) + if err != ErrMissingCertBlob { + t.Errorf("bad error: %s", err) + } +} + +func TestClient_DeleteCustomTLSCertificate_validation(t *testing.T) { + t.Parallel() + + var err error + record(t, "custom_tls/delete", func(c *Client) { + err = c.DeleteCustomTLSCertificate(&DeleteCustomTLSCertificateInput{ + ID: "CERTIFICATE_ID", + }) + }) + if err != nil { + t.Fatal(err) + } + + err = testClient.DeleteCustomTLSCertificate(&DeleteCustomTLSCertificateInput{}) + if err != ErrMissingID { + t.Errorf("bad error: %s", err) + } +} + +func TestClient_ListCustomTLSCertificates_validation(t *testing.T) { + t.Parallel() + + var err error + record(t, "custom_tls/list", func(c *Client) { + _, err = c.ListCustomTLSCertificates(&ListCustomTLSCertificatesInput{}) + }) + if err != nil { + t.Fatal(err) + } +} + +func TestClient_GetCustomTLSCertificate_validation(t *testing.T) { + t.Parallel() + + var err error + record(t, "custom_tls/get", func(c *Client) { + _, err = c.GetCustomTLSCertificate(&GetCustomTLSCertificateInput{ + ID: "CERTIFICATE_ID", + }) + }) + if err != nil { + t.Fatal(err) + } + + _, err = testClient.GetCustomTLSCertificate(&GetCustomTLSCertificateInput{}) + if err != ErrMissingID { + t.Errorf("bad error: %s", err) + } +} + +func TestClient_UpdateCustomTLSCertificate_validation(t *testing.T) { + t.Parallel() + + var err error + record(t, "custom_tls/update", func(c *Client) { + _, err = c.UpdateCustomTLSCertificate(&UpdateCustomTLSCertificateInput{ + ID: "CERTIFICATE_ID", + CertBlob: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n", + Name: "My certificate", + }) + }) + if err != nil { + t.Fatal(err) + } + + _, err = testClient.UpdateCustomTLSCertificate(&UpdateCustomTLSCertificateInput{ + CertBlob: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n", + Name: "My certificate", + }) + if err != ErrMissingID { + t.Errorf("bad error: %s", err) + } + + _, err = testClient.UpdateCustomTLSCertificate(&UpdateCustomTLSCertificateInput{ + ID: "CERTIFICATE_ID", + Name: "My certificate", + }) + if err != ErrMissingCertBlob { + t.Errorf("bad error: %s", err) + } + + _, err = testClient.UpdateCustomTLSCertificate(&UpdateCustomTLSCertificateInput{ + ID: "CERTIFICATE_ID", + CertBlob: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n", + }) + if err != ErrMissingName { + t.Errorf("bad error: %s", err) + } +} diff --git a/fastly/custom_tls_configuration.go b/fastly/custom_tls_configuration.go new file mode 100644 index 000000000..3e5d54574 --- /dev/null +++ b/fastly/custom_tls_configuration.go @@ -0,0 +1,163 @@ +package fastly + +import ( + "fmt" + "reflect" + "strconv" + "time" + + "github.com/google/jsonapi" +) + +// CustomTLSConfiguration represents a TLS configuration response from the Fastly API. +type CustomTLSConfiguration struct { + ID string `jsonapi:"primary,tls_configuration"` + DNSRecords []*DNSRecord `jsonapi:"relation,dns_records"` + Bulk bool `jsonapi:"attr,bulk"` + Default bool `jsonapi:"attr,default"` + HTTPProtocols []string `jsonapi:"attr,http_protocols"` + Name string `jsonapi:"attr,name"` + TLSProtocols []string `jsonapi:"attr,tls_protocols"` + CreatedAt *time.Time `jsonapi:"attr,created_at,iso8601"` + UpdatedAt *time.Time `jsonapi:"attr,updated_at,iso8601"` +} + +// DNSRecord is a child of CustomTLSConfiguration +type DNSRecord struct { + ID string `jsonapi:"primary,dns_record"` + RecordType string `jsonapi:"attr,record_type"` + Region string `jsonapi:"attr,region"` +} + +// ListCustomTLSConfigurationsInput is used as input to the ListCustomTLSConfigurationsInput function. +type ListCustomTLSConfigurationsInput struct { + FilterBulk bool // Whether or not to only include bulk=true configurations + Include string // Include related objects. Optional, comma-separated values. Permitted values: dns_records. + PageNumber int // The page index for pagination. + PageSize int // The number of keys per page. + +} + +// formatFilters converts user input into query parameters for filtering. +func (i *ListCustomTLSConfigurationsInput) formatFilters() map[string]string { + result := map[string]string{} + pairings := map[string]interface{}{ + "filter[bulk]": i.FilterBulk, + "include": i.Include, + "page[size]": i.PageSize, + "page[number]": i.PageNumber, + } + + for key, value := range pairings { + switch t := reflect.TypeOf(value).String(); t { + case "string": + if value != "" { + result[key] = value.(string) + } + case "int": + if value != 0 { + result[key] = strconv.Itoa(value.(int)) + } + } + } + + return result +} + +// ListCustomTLSConfigurations list all TLS configurations. +func (c *Client) ListCustomTLSConfigurations(i *ListCustomTLSConfigurationsInput) ([]*CustomTLSConfiguration, error) { + p := "/tls/configurations" + ro := &RequestOptions{ + Params: i.formatFilters(), + Headers: map[string]string{ + "Accept": "application/vnd.api+json", // this is required otherwise the filters don't work + }, + } + + r, err := c.Get(p, ro) + if err != nil { + return nil, err + } + + data, err := jsonapi.UnmarshalManyPayload(r.Body, reflect.TypeOf(new(CustomTLSConfiguration))) + if err != nil { + return nil, err + } + + con := make([]*CustomTLSConfiguration, len(data)) + for i := range data { + typed, ok := data[i].(*CustomTLSConfiguration) + if !ok { + return nil, fmt.Errorf("unexpected response type: %T", data[i]) + } + con[i] = typed + } + + return con, nil +} + +// GetCustomTLSConfigurationInput is used as input to the GetCustomTLSConfiguration function. +type GetCustomTLSConfigurationInput struct { + ID string + Include string // Include related objects. Optional, comma-separated values. Permitted values: dns_records. +} + +// GetCustomTLSConfiguration returns a single TLS configuration. +func (c *Client) GetCustomTLSConfiguration(i *GetCustomTLSConfigurationInput) (*CustomTLSConfiguration, error) { + if i.ID == "" { + return nil, ErrMissingID + } + + p := fmt.Sprintf("/tls/configurations/%s", i.ID) + + ro := &RequestOptions{ + Headers: map[string]string{ + "Accept": "application/vnd.api+json", // this is required otherwise the params don't work + }, + } + + if i.Include != "" { + ro.Params = map[string]string{"include": i.Include} + } + + r, err := c.Get(p, ro) + if err != nil { + return nil, err + } + + var con CustomTLSConfiguration + if err := jsonapi.UnmarshalPayload(r.Body, &con); err != nil { + return nil, err + } + + return &con, nil +} + +// UpdateCustomTLSConfigurationInput is used as input to the UpdateCustomTLSConfiguration function. +type UpdateCustomTLSConfigurationInput struct { + ID string + Name string `jsonapi:"attr,name"` +} + +// UpdateCustomTLSConfiguration can only be used to change the name of the configuration +func (c *Client) UpdateCustomTLSConfiguration(i *UpdateCustomTLSConfigurationInput) (*CustomTLSConfiguration, error) { + if i.ID == "" { + return nil, ErrMissingID + } + + if i.Name == "" { + return nil, ErrMissingName + } + + path := fmt.Sprintf("/tls/configurations/%s", i.ID) + resp, err := c.PatchJSONAPI(path, i, nil) + if err != nil { + return nil, err + } + + var con CustomTLSConfiguration + if err := jsonapi.UnmarshalPayload(resp.Body, &con); err != nil { + return nil, err + } + return &con, nil +} diff --git a/fastly/custom_tls_configuration_test.go b/fastly/custom_tls_configuration_test.go new file mode 100644 index 000000000..d6ad79193 --- /dev/null +++ b/fastly/custom_tls_configuration_test.go @@ -0,0 +1,118 @@ +package fastly + +import "testing" + +func TestClient_CustomTLSConfiguration(t *testing.T) { + t.Parallel() + + fixtureBase := "custom_tls_configuration/" + + var err error + conID := "TLS_CONFIGURATION_ID" + + // Get + var gcon *CustomTLSConfiguration + record(t, fixtureBase+"get", func(c *Client) { + gcon, err = c.GetCustomTLSConfiguration(&GetCustomTLSConfigurationInput{ + ID: conID, + }) + }) + if err != nil { + t.Fatal(err) + } + if conID != gcon.ID { + t.Errorf("bad ID: %q (%q)", conID, gcon.ID) + } + + // List + var lcon []*CustomTLSConfiguration + record(t, fixtureBase+"list", func(c *Client) { + lcon, err = c.ListCustomTLSConfigurations(&ListCustomTLSConfigurationsInput{}) + }) + if err != nil { + t.Fatal(err) + } + if len(lcon) < 1 { + t.Errorf("bad tls configurations: %v", lcon) + } + + // Update + var ucon *CustomTLSConfiguration + newName := "My configuration v2" + record(t, fixtureBase+"update", func(c *Client) { + ucon, err = c.UpdateCustomTLSConfiguration(&UpdateCustomTLSConfigurationInput{ + ID: "TLS_CONFIGURATION_ID", + Name: newName, + }) + }) + if err != nil { + t.Fatal(err) + } + if conID != ucon.ID { + t.Errorf("bad ID: %q (%q)", conID, ucon.ID) + } + if ucon.Name != newName { + t.Errorf("bad Name: %q (%q)", newName, ucon.Name) + } + +} + +func TestClient_ListCustomTLSConfigurations_validation(t *testing.T) { + t.Parallel() + + var err error + record(t, "custom_tls_configuration/list", func(c *Client) { + _, err = c.ListCustomTLSConfigurations(&ListCustomTLSConfigurationsInput{}) + }) + if err != nil { + t.Fatal(err) + } +} + +func TestClient_GetCustomTLSConfiguration_validation(t *testing.T) { + t.Parallel() + + var err error + record(t, "custom_tls_configuration/get", func(c *Client) { + _, err = c.GetCustomTLSConfiguration(&GetCustomTLSConfigurationInput{ + ID: "TLS_CONFIGURATION_ID", + }) + }) + if err != nil { + t.Fatal(err) + } + + _, err = testClient.GetCustomTLSConfiguration(&GetCustomTLSConfigurationInput{}) + if err != ErrMissingID { + t.Errorf("bad error: %s", err) + } +} + +func TestClient_UpdateCustomTLSConfiguration_validation(t *testing.T) { + t.Parallel() + + var err error + record(t, "custom_tls_configuration/update", func(c *Client) { + _, err = c.UpdateCustomTLSConfiguration(&UpdateCustomTLSConfigurationInput{ + ID: "TLS_CONFIGURATION_ID", + Name: "My configuration v2", + }) + }) + if err != nil { + t.Fatal(err) + } + + _, err = testClient.UpdateCustomTLSConfiguration(&UpdateCustomTLSConfigurationInput{ + Name: "My configuration v2", + }) + if err != ErrMissingID { + t.Errorf("bad error: %s", err) + } + + _, err = testClient.UpdateCustomTLSConfiguration(&UpdateCustomTLSConfigurationInput{ + ID: "CONFIGURATION_ID", + }) + if err != ErrMissingName { + t.Errorf("bad error: %s", err) + } +} diff --git a/fastly/errors.go b/fastly/errors.go index cbab4b9e5..c426f768c 100644 --- a/fastly/errors.go +++ b/fastly/errors.go @@ -169,6 +169,18 @@ var ErrMissingCertBlob = errors.New("missing required field 'CertBlob'") // a "IntermediatesBlob" key, but one was not set. var ErrMissingIntermediatesBlob = errors.New("missing required field 'IntermediatesBlob'") +// ErrMissingTLSCertificate is an error that is returned from an input struct that requires +// a "Certificate" field assigned a "TLSCertificate" struct, but one was not set. +var ErrMissingTLSCertificate = errors.New("missing required field 'Certificate'") + +// ErrMissingTLSConfiguration is an error that is returned from an input struct that requires +// a "Configuration" field assigned a "TLSConfiguration" struct, but one was not set. +var ErrMissingTLSConfiguration = errors.New("missing required field 'Configuration'") + +// ErrMissingTLSDomain is an error that is returned from an input struct that requires +// a "Domain" field assigned a "TLSDomain" struct, but one was not set. +var ErrMissingTLSDomain = errors.New("missing required field 'Domain'") + // ErrStatusNotOk is an error that indicates that indicates that the response body returned // by the Fastly API was not `{"status": "ok"}` var ErrStatusNotOk = errors.New("unexpected 'status' field in API response body") diff --git a/fastly/fixtures/custom_tls/cleanup.yaml b/fastly/fixtures/custom_tls/cleanup.yaml new file mode 100644 index 000000000..978488f0c --- /dev/null +++ b/fastly/fixtures/custom_tls/cleanup.yaml @@ -0,0 +1,37 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/certificates/CERTIFICATE_ID + method: DELETE + response: + body: "" + headers: + Accept-Ranges: + - bytes + Date: + - Sat, 11 May 2019 21:45:02 GMT + Status: + - 204 No Content + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish + - 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9035-CONTROL-SLWDC, cache-lax8631-LAX + X-Timer: + - S1557611103.608075,VS0,VE254 + status: 204 No Content + code: 204 diff --git a/fastly/fixtures/custom_tls/create.yaml b/fastly/fixtures/custom_tls/create.yaml new file mode 100644 index 000000000..10634a5dd --- /dev/null +++ b/fastly/fixtures/custom_tls/create.yaml @@ -0,0 +1,50 @@ +--- +version: 1 +interactions: +- request: + body: | + {"data":{"type":"","attributes":{"cert_blob":"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n","name":"My certificate"}}} + form: {} + headers: + Accept: + - application/vnd.api+json + Content-Type: + - application/vnd.api+json + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/certificates + method: POST + response: + body: '{"data":{"id":"CERTIFICATE_ID","type":"tls_certificate","attributes":{"created_at":"2020-10-21T17:39:36.000Z","issued_to":"ISSUED_TO","issuer":"ISSUER","name":"My certificate","not_after":"2021-11-14T17:21:03.000Z","not_before":"2020-10-13T17:21:03.000Z","replace":false,"serial_number":"00000000000000000","signature_algorithm":"SHA256-RSA","updated_at":"2020-10-21T17:39:36.000Z"},"relationships":{"tls_domains":{"data":[{"id":"DOMAIN_NAME","type":"tls_domain"}]}}}}' + headers: + Accept-Ranges: + - bytes + Cache-Control: + - no-cache + Content-Type: + - application/json + Date: + - Thu, 06 Jun 2019 22:12:54 GMT + Fastly-Ratelimit-Remaining: + - "980" + Fastly-Ratelimit-Reset: + - "1559862000" + Status: + - 200 OK + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + - 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Served-By: + - cache-control-slwdc9035-CONTROL-SLWDC, cache-sjc3621-SJC + X-Timer: + - S1559859174.118436,VS0,VE554 + status: 200 OK + code: 200 diff --git a/fastly/fixtures/custom_tls/delete.yaml b/fastly/fixtures/custom_tls/delete.yaml new file mode 100644 index 000000000..978488f0c --- /dev/null +++ b/fastly/fixtures/custom_tls/delete.yaml @@ -0,0 +1,37 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/certificates/CERTIFICATE_ID + method: DELETE + response: + body: "" + headers: + Accept-Ranges: + - bytes + Date: + - Sat, 11 May 2019 21:45:02 GMT + Status: + - 204 No Content + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish + - 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9035-CONTROL-SLWDC, cache-lax8631-LAX + X-Timer: + - S1557611103.608075,VS0,VE254 + status: 204 No Content + code: 204 diff --git a/fastly/fixtures/custom_tls/get.yaml b/fastly/fixtures/custom_tls/get.yaml new file mode 100644 index 000000000..29ddb4ad4 --- /dev/null +++ b/fastly/fixtures/custom_tls/get.yaml @@ -0,0 +1,43 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/certificates/CERTIFICATE_ID + method: GET + response: + body: '{"data":{"id":"CERTIFICATE_ID","type":"tls_certificate","attributes":{"created_at":"2020-10-21T17:39:36.000Z","issued_to":"ISSUED_TO","issuer":"ISSUER","name":"My certificate","not_after":"2021-11-14T17:21:03.000Z","not_before":"2020-10-13T17:21:03.000Z","replace":false,"serial_number":"00000000000000000","signature_algorithm":"SHA256-RSA","updated_at":"2020-10-21T17:39:36.000Z"},"relationships":{"tls_domains":{"data":[{"id":"DOMAIN_NAME","type":"tls_domain"}]}}}}' + headers: + Accept-Ranges: + - bytes + Age: + - "0" + Content-Type: + - application/json + Date: + - Sat, 11 May 2019 03:33:50 GMT + Status: + - 200 OK + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + - 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9035-CONTROL-SLWDC, cache-lax8622-LAX + X-Timer: + - S1557545630.411550,VS0,VE560 + status: 200 OK + code: 200 diff --git a/fastly/fixtures/custom_tls/list.yaml b/fastly/fixtures/custom_tls/list.yaml new file mode 100644 index 000000000..4c7f49cd4 --- /dev/null +++ b/fastly/fixtures/custom_tls/list.yaml @@ -0,0 +1,45 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/vnd.api+json + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/certificates + method: GET + response: + body: '{"data": [{"data":{"id":"CERTIFICATE_ID","type":"tls_certificate","attributes":{"created_at":"2020-10-21T17:39:36.000Z","issued_to":"ISSUED_TO","issuer":"ISSUER","name":"My certificate","not_after":"2021-11-14T17:21:03.000Z","not_before":"2020-10-13T17:21:03.000Z","replace":false,"serial_number":"00000000000000000","signature_algorithm":"SHA256-RSA","updated_at":"2020-10-21T17:39:36.000Z"},"relationships":{"tls_domains":{"data":[{"id":"DOMAIN_NAME","type":"tls_domain"}]}}}}]}' + headers: + Accept-Ranges: + - bytes + Age: + - "0" + Content-Type: + - application/json + Date: + - Sat, 11 May 2019 03:33:50 GMT + Status: + - 200 OK + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + - 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9035-CONTROL-SLWDC, cache-lax8622-LAX + X-Timer: + - S1557545630.411550,VS0,VE560 + status: 200 OK + code: 200 diff --git a/fastly/fixtures/custom_tls/update.yaml b/fastly/fixtures/custom_tls/update.yaml new file mode 100644 index 000000000..5c7ddeb36 --- /dev/null +++ b/fastly/fixtures/custom_tls/update.yaml @@ -0,0 +1,46 @@ +--- +version: 1 +interactions: +- request: + body: | + {"data":{"type":"","attributes":{"cert_blob":"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n","id":"CERTIFICATE_ID","name":"My certificate"}}} + form: {} + headers: + Accept: + - application/vnd.api+json + Content-Type: + - application/vnd.api+json + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/certificates/CERTIFICATE_ID + method: PATCH + response: + body: '{"data":{"id":"CERTIFICATE_ID","type":"tls_certificate","attributes":{"created_at":"2020-10-21T17:39:36.000Z","issued_to":"ISSUED_TO","issuer":"ISSUER","name":"My certificate","not_after":"2021-11-14T17:21:03.000Z","not_before":"2020-10-13T17:21:03.000Z","replace":false,"serial_number":"00000000000000000","signature_algorithm":"SHA256-RSA","updated_at":"2020-10-21T17:39:36.000Z"},"relationships":{"tls_domains":{"data":[{"id":"DOMAIN_NAME","type":"tls_domain"}]}}}}' + headers: + Accept-Ranges: + - bytes + Content-Length: + - "890" + Content-Type: + - application/vnd.api+json + Date: + - Sat, 11 May 2019 21:45:02 GMT + Status: + - 200 OK + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish + - 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9036-CONTROL-SLWDC, cache-lax8631-LAX + X-Timer: + - S1557611102.061591,VS0,VE521 + status: 200 OK + code: 200 diff --git a/fastly/fixtures/custom_tls_activation/cleanup.yaml b/fastly/fixtures/custom_tls_activation/cleanup.yaml new file mode 100644 index 000000000..1728eb4e9 --- /dev/null +++ b/fastly/fixtures/custom_tls_activation/cleanup.yaml @@ -0,0 +1,43 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/activations/ACTIVATION_ID + method: DELETE + response: + body: '{}' + headers: + Accept-Ranges: + - bytes + - bytes + Content-Length: + - "55" + Content-Type: + - application/vnd.api+json + Date: + - Fri, 30 Oct 2020 19:40:25 GMT + Status: + - 200 OK + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish + - 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9035-CONTROL-SLWDC, cache-sea4475-SEA + X-Timer: + - S1604086825.126245,VS0,VE103 + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/custom_tls_activation/create.yaml b/fastly/fixtures/custom_tls_activation/create.yaml new file mode 100644 index 000000000..385f9c327 --- /dev/null +++ b/fastly/fixtures/custom_tls_activation/create.yaml @@ -0,0 +1,48 @@ +--- +version: 1 +interactions: +- request: + body: | + {"data":{"type":"","relationships":{"tls_certificate":{"data":{"type":"tls_certificate"}},"tls_configuration":{"data":{"type":"tls_configuration"}},"tls_domain":{"data":{"type":"tls_domain"}}}},"included":[{"type":"tls_certificate","attributes":{"type":""}},{"type":"tls_configuration","attributes":{"type":""}},{"type":"tls_domain","attributes":{"type":""}}]} + form: {} + headers: + Accept: + - application/vnd.api+json + Content-Type: + - application/vnd.api+json + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/activations + method: POST + response: + body: '{"data":{"id":"ACTIVATION_ID","type":"tls_activation","attributes":{"created_at":"2020-10-15T15:50:17.000Z"},"relationships":{"tls_certificate":{"data":{"id":"CERTIFICATE_ID","type":"tls_certificate"}},"tls_configuration":{"data":{"id":"CONFIGURATION_ID","type":"tls_configuration"}},"tls_domain":{"data":{"id":"DOMAIN_NAME","type":"tls_domain"}}}}}' + headers: + Accept-Ranges: + - bytes + - bytes + Content-Length: + - "55" + Content-Type: + - application/vnd.api+json + Date: + - Thu, 29 Oct 2020 21:56:36 GMT + Status: + - 200 OK + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish + - 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9037-CONTROL-SLWDC, cache-sea4479-SEA + X-Timer: + - S1604008596.366056,VS0,VE70 + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/custom_tls_activation/delete.yaml b/fastly/fixtures/custom_tls_activation/delete.yaml new file mode 100644 index 000000000..08842cc17 --- /dev/null +++ b/fastly/fixtures/custom_tls_activation/delete.yaml @@ -0,0 +1,43 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/activations/ACTIVATION_ID + method: DELETE + response: + body: '{}' + headers: + Accept-Ranges: + - bytes + - bytes + Content-Length: + - "55" + Content-Type: + - application/vnd.api+json + Date: + - Thu, 29 Oct 2020 21:56:36 GMT + Status: + - 200 OK + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish + - 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9037-CONTROL-SLWDC, cache-sea4479-SEA + X-Timer: + - S1604008596.365429,VS0,VE113 + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/custom_tls_activation/get.yaml b/fastly/fixtures/custom_tls_activation/get.yaml new file mode 100644 index 000000000..542764bd1 --- /dev/null +++ b/fastly/fixtures/custom_tls_activation/get.yaml @@ -0,0 +1,43 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/activations/ACTIVATION_ID + method: GET + response: + body: '{"data":{"id":"ACTIVATION_ID","type":"tls_activation","attributes":{"created_at":"2020-10-15T15:50:17.000Z"},"relationships":{"tls_certificate":{"data":{"id":"CERTIFICATE_ID","type":"tls_certificate"}},"tls_configuration":{"data":{"id":"CONFIGURATION_ID","type":"tls_configuration"}},"tls_domain":{"data":{"id":"DOMAIN_NAME","type":"tls_domain"}}}}}' + headers: + Accept-Ranges: + - bytes + - bytes + Content-Length: + - "55" + Content-Type: + - application/vnd.api+json + Date: + - Thu, 29 Oct 2020 21:56:36 GMT + Status: + - 200 OK + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish + - 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9037-CONTROL-SLWDC, cache-sea4479-SEA + X-Timer: + - S1604008596.366056,VS0,VE70 + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/custom_tls_activation/list.yaml b/fastly/fixtures/custom_tls_activation/list.yaml new file mode 100644 index 000000000..616910223 --- /dev/null +++ b/fastly/fixtures/custom_tls_activation/list.yaml @@ -0,0 +1,45 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/vnd.api+json + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/activations + method: GET + response: + body: '{"data":[{"id":"ACTIVATION_ID","type":"tls_activation","attributes":{"created_at":"2020-10-15T15:50:17.000Z"},"relationships":{"tls_certificate":{"data":{"id":"CERTIFICATE_ID","type":"tls_certificate"}},"tls_configuration":{"data":{"id":"CONFIGURATION_ID","type":"tls_configuration"}},"tls_domain":{"data":{"id":"DOMAIN_NAME","type":"tls_domain"}}}},{"id":"ACTIVATION_ID","type":"tls_activation","attributes":{"created_at":"2020-10-15T15:49:05.000Z"},"relationships":{"tls_certificate":{"data":{"id":"CERTIFICATE_ID","type":"tls_certificate"}},"tls_configuration":{"data":{"id":"CONFIGURATION_ID","type":"tls_configuration"}},"tls_domain":{"data":{"id":"DOMAIN_NAME","type":"tls_domain"}}}},{"id":"ACTIVATION_ID","type":"tls_activation","attributes":{"created_at":"2020-10-14T21:08:30.000Z"},"relationships":{"tls_certificate":{"data":{"id":"CERTIFICATE_ID","type":"tls_certificate"}},"tls_configuration":{"data":{"id":"CONFIGURATION_ID","type":"tls_configuration"}},"tls_domain":{"data":{"id":"DOMAIN_NAME","type":"tls_domain"}}}}]}' + headers: + Accept-Ranges: + - bytes + - bytes + Content-Length: + - "55" + Content-Type: + - application/vnd.api+json + Date: + - Thu, 29 Oct 2020 21:56:36 GMT + Status: + - 200 OK + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish + - 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9035-CONTROL-SLWDC, cache-sea4479-SEA + X-Timer: + - S1604008596.365928,VS0,VE104 + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/custom_tls_activation/update.yaml b/fastly/fixtures/custom_tls_activation/update.yaml new file mode 100644 index 000000000..96b05283e --- /dev/null +++ b/fastly/fixtures/custom_tls_activation/update.yaml @@ -0,0 +1,48 @@ +--- +version: 1 +interactions: +- request: + body: | + {"data":{"type":"","attributes":{"id":"ACTIVATION_ID"},"relationships":{"tls_certificate":{"data":{"type":"tls_certificate"}}}},"included":[{"type":"tls_certificate","attributes":{"type":""}}]} + form: {} + headers: + Accept: + - application/vnd.api+json + Content-Type: + - application/vnd.api+json + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/activations/ACTIVATION_ID + method: PATCH + response: + body: '{"data":{"id":"ACTIVATION_ID","type":"tls_activation","attributes":{"created_at":"2020-10-15T15:50:17.000Z"},"relationships":{"tls_certificate":{"data":{"id":"CERTIFICATE_ID","type":"tls_certificate"}},"tls_configuration":{"data":{"id":"CONFIGURATION_ID","type":"tls_configuration"}},"tls_domain":{"data":{"id":"DOMAIN_NAME","type":"tls_domain"}}}}}' + headers: + Accept-Ranges: + - bytes + - bytes + Content-Length: + - "55" + Content-Type: + - application/vnd.api+json + Date: + - Thu, 29 Oct 2020 21:56:36 GMT + Status: + - 200 OK + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish + - 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9035-CONTROL-SLWDC, cache-sea4479-SEA + X-Timer: + - S1604008596.366085,VS0,VE103 + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/custom_tls_configuration/get.yaml b/fastly/fixtures/custom_tls_configuration/get.yaml new file mode 100644 index 000000000..7495ad456 --- /dev/null +++ b/fastly/fixtures/custom_tls_configuration/get.yaml @@ -0,0 +1,43 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/vnd.api+json + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/configurations/TLS_CONFIGURATION_ID + method: GET + response: + body: '{"data":{"id":"TLS_CONFIGURATION_ID","type":"tls_configuration","attributes":{"bulk":false,"created_at":"2018-09-11T20:59:51.000Z","default":true,"http_protocols":["http/1.1","http/2"],"name":"My configuration","tls_protocols":["1.2"],"updated_at":"2020-10-20T22:16:11.000Z"},"relationships":{"dns_records":{"data":[{"id":"IP_ADDRESS","type":"dns_record"}]}}},"included":[{"id":"IP_ADDRESS","type":"dns_record","attributes":{"record_type":"A","region":"global"}}]}' + headers: + Accept-Ranges: + - bytes + Content-Length: + - "55" + Content-Type: + - application/vnd.api+json + Date: + - Thu, 22 Oct 2020 22:07:52 GMT + Status: + - 200 OK + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish, 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9036-CONTROL-SLWDC, cache-sea4481-SEA + X-Timer: + - S1603404473.590476,VS0,VE111 + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/custom_tls_configuration/list.yaml b/fastly/fixtures/custom_tls_configuration/list.yaml new file mode 100644 index 000000000..36a8c364f --- /dev/null +++ b/fastly/fixtures/custom_tls_configuration/list.yaml @@ -0,0 +1,46 @@ +--- +version: 1 +interactions: +- request: + body: "" + form: {} + headers: + Accept: + - application/vnd.api+json + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/configurations + method: GET + response: + body: '{"data":[{"id":"TLS_CONFIGURATION_ID","type":"tls_configuration","attributes":{"bulk":false,"created_at":"2018-09-11T20:59:51.000Z","default":true,"http_protocols":["http/1.1","http/2"],"name":"My configuration","tls_protocols":["1.2"],"updated_at":"2020-10-20T22:16:11.000Z"}}]}' + headers: + Accept-Ranges: + - bytes + - bytes + - bytes + Content-Length: + - "55" + Content-Type: + - application/vnd.api+json + Date: + - Thu, 22 Oct 2020 22:34:56 GMT + Status: + - 200 OK + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish + - 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9036-CONTROL-SLWDC, cache-sea4463-SEA + X-Timer: + - S1603406096.136249,VS0,VE107 + status: 200 OK + code: 200 + duration: "" diff --git a/fastly/fixtures/custom_tls_configuration/update.yaml b/fastly/fixtures/custom_tls_configuration/update.yaml new file mode 100644 index 000000000..2c1e821ad --- /dev/null +++ b/fastly/fixtures/custom_tls_configuration/update.yaml @@ -0,0 +1,46 @@ +--- +version: 1 +interactions: +- request: + body: | + {"data":{"type":"","attributes":{"id":"TLS_CONFIGURATION_ID","name":"My configuration v2"}}} + form: {} + headers: + Accept: + - application/vnd.api+json + Content-Type: + - application/vnd.api+json + User-Agent: + - FastlyGo/1.17.0 (+github.com/fastly/go-fastly; go1.14.2) + url: https://api.fastly.com/tls/configurations/TLS_CONFIGURATION_ID + method: PATCH + response: + body: '{"data":{"id":"TLS_CONFIGURATION_ID","type":"tls_configuration","attributes":{"bulk":false,"created_at":"2018-09-11T20:59:51.000Z","default":true,"http_protocols":["http/1.1","http/2"],"name":"My configuration v2","tls_protocols":["1.2"],"updated_at":"2020-10-22T22:38:24.000Z"}}}' + headers: + Accept-Ranges: + - bytes + Content-Length: + - "55" + Content-Type: + - application/vnd.api+json + Date: + - Thu, 22 Oct 2020 22:37:37 GMT + Status: + - 200 OK + Strict-Transport-Security: + - max-age=31536000 + Via: + - 1.1 varnish, 1.1 varnish + X-Cache: + - MISS, MISS + X-Cache-Hits: + - 0, 0 + X-Content-Type-Options: + - nosniff + X-Served-By: + - cache-control-slwdc9035-CONTROL-SLWDC, cache-sea4469-SEA + X-Timer: + - S1603406257.897746,VS0,VE113 + status: 200 OK + code: 200 + duration: ""