1package cloudflare
2
3import (
4	"context"
5	"encoding/json"
6	"fmt"
7	"net/http"
8	"time"
9
10	"github.com/pkg/errors"
11)
12
13// ZoneCustomSSL represents custom SSL certificate metadata.
14type ZoneCustomSSL struct {
15	ID              string                       `json:"id"`
16	Hosts           []string                     `json:"hosts"`
17	Issuer          string                       `json:"issuer"`
18	Signature       string                       `json:"signature"`
19	Status          string                       `json:"status"`
20	BundleMethod    string                       `json:"bundle_method"`
21	GeoRestrictions ZoneCustomSSLGeoRestrictions `json:"geo_restrictions"`
22	ZoneID          string                       `json:"zone_id"`
23	UploadedOn      time.Time                    `json:"uploaded_on"`
24	ModifiedOn      time.Time                    `json:"modified_on"`
25	ExpiresOn       time.Time                    `json:"expires_on"`
26	Priority        int                          `json:"priority"`
27	KeylessServer   KeylessSSL                   `json:"keyless_server"`
28}
29
30// ZoneCustomSSLGeoRestrictions represents the parameter to create or update
31// geographic restrictions on a custom ssl certificate.
32type ZoneCustomSSLGeoRestrictions struct {
33	Label string `json:"label"`
34}
35
36// zoneCustomSSLResponse represents the response from the zone SSL details endpoint.
37type zoneCustomSSLResponse struct {
38	Response
39	Result ZoneCustomSSL `json:"result"`
40}
41
42// zoneCustomSSLsResponse represents the response from the zone SSL list endpoint.
43type zoneCustomSSLsResponse struct {
44	Response
45	Result []ZoneCustomSSL `json:"result"`
46}
47
48// ZoneCustomSSLOptions represents the parameters to create or update an existing
49// custom SSL configuration.
50type ZoneCustomSSLOptions struct {
51	Certificate     string                        `json:"certificate"`
52	PrivateKey      string                        `json:"private_key"`
53	BundleMethod    string                        `json:"bundle_method,omitempty"`
54	GeoRestrictions *ZoneCustomSSLGeoRestrictions `json:"geo_restrictions,omitempty"`
55	Type            string                        `json:"type,omitempty"`
56}
57
58// ZoneCustomSSLPriority represents a certificate's ID and priority. It is a
59// subset of ZoneCustomSSL used for patch requests.
60type ZoneCustomSSLPriority struct {
61	ID       string `json:"ID"`
62	Priority int    `json:"priority"`
63}
64
65// CreateSSL allows you to add a custom SSL certificate to the given zone.
66//
67// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-create-ssl-configuration
68func (api *API) CreateSSL(ctx context.Context, zoneID string, options ZoneCustomSSLOptions) (ZoneCustomSSL, error) {
69	uri := fmt.Sprintf("/zones/%s/custom_certificates", zoneID)
70	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, options)
71	if err != nil {
72		return ZoneCustomSSL{}, err
73	}
74	var r zoneCustomSSLResponse
75	if err := json.Unmarshal(res, &r); err != nil {
76		return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError)
77	}
78	return r.Result, nil
79}
80
81// ListSSL lists the custom certificates for the given zone.
82//
83// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-list-ssl-configurations
84func (api *API) ListSSL(ctx context.Context, zoneID string) ([]ZoneCustomSSL, error) {
85	uri := fmt.Sprintf("/zones/%s/custom_certificates", zoneID)
86	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
87	if err != nil {
88		return nil, err
89	}
90	var r zoneCustomSSLsResponse
91	if err := json.Unmarshal(res, &r); err != nil {
92		return nil, errors.Wrap(err, errUnmarshalError)
93	}
94	return r.Result, nil
95}
96
97// SSLDetails returns the configuration details for a custom SSL certificate.
98//
99// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-ssl-configuration-details
100func (api *API) SSLDetails(ctx context.Context, zoneID, certificateID string) (ZoneCustomSSL, error) {
101	uri := fmt.Sprintf("/zones/%s/custom_certificates/%s", zoneID, certificateID)
102	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
103	if err != nil {
104		return ZoneCustomSSL{}, err
105	}
106	var r zoneCustomSSLResponse
107	if err := json.Unmarshal(res, &r); err != nil {
108		return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError)
109	}
110	return r.Result, nil
111}
112
113// UpdateSSL updates (replaces) a custom SSL certificate.
114//
115// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-update-ssl-configuration
116func (api *API) UpdateSSL(ctx context.Context, zoneID, certificateID string, options ZoneCustomSSLOptions) (ZoneCustomSSL, error) {
117	uri := fmt.Sprintf("/zones/%s/custom_certificates/%s", zoneID, certificateID)
118	res, err := api.makeRequestContext(ctx, http.MethodPatch, uri, options)
119	if err != nil {
120		return ZoneCustomSSL{}, err
121	}
122	var r zoneCustomSSLResponse
123	if err := json.Unmarshal(res, &r); err != nil {
124		return ZoneCustomSSL{}, errors.Wrap(err, errUnmarshalError)
125	}
126	return r.Result, nil
127}
128
129// ReprioritizeSSL allows you to change the priority (which is served for a given
130// request) of custom SSL certificates associated with the given zone.
131//
132// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-re-prioritize-ssl-certificates
133func (api *API) ReprioritizeSSL(ctx context.Context, zoneID string, p []ZoneCustomSSLPriority) ([]ZoneCustomSSL, error) {
134	uri := fmt.Sprintf("/zones/%s/custom_certificates/prioritize", zoneID)
135	params := struct {
136		Certificates []ZoneCustomSSLPriority `json:"certificates"`
137	}{
138		Certificates: p,
139	}
140	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params)
141	if err != nil {
142		return nil, err
143	}
144	var r zoneCustomSSLsResponse
145	if err := json.Unmarshal(res, &r); err != nil {
146		return nil, errors.Wrap(err, errUnmarshalError)
147	}
148	return r.Result, nil
149}
150
151// DeleteSSL deletes a custom SSL certificate from the given zone.
152//
153// API reference: https://api.cloudflare.com/#custom-ssl-for-a-zone-delete-an-ssl-certificate
154func (api *API) DeleteSSL(ctx context.Context, zoneID, certificateID string) error {
155	uri := fmt.Sprintf("/zones/%s/custom_certificates/%s", zoneID, certificateID)
156	if _, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil); err != nil {
157		return err
158	}
159	return nil
160}
161