1package cloudflare 2 3import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "net/http" 9 "net/url" 10 "time" 11 12 "github.com/pkg/errors" 13) 14 15// OriginCACertificate represents a Cloudflare-issued certificate. 16// 17// API reference: https://api.cloudflare.com/#cloudflare-ca 18type OriginCACertificate struct { 19 ID string `json:"id"` 20 Certificate string `json:"certificate"` 21 Hostnames []string `json:"hostnames"` 22 ExpiresOn time.Time `json:"expires_on"` 23 RequestType string `json:"request_type"` 24 RequestValidity int `json:"requested_validity"` 25 RevokedAt time.Time `json:"revoked_at,omitempty"` 26 CSR string `json:"csr"` 27} 28 29// UnmarshalJSON handles custom parsing from an API response to an OriginCACertificate 30// http://choly.ca/post/go-json-marshalling/ 31func (c *OriginCACertificate) UnmarshalJSON(data []byte) error { 32 type alias OriginCACertificate 33 34 aux := &struct { 35 ExpiresOn string `json:"expires_on"` 36 *alias 37 }{ 38 alias: (*alias)(c), 39 } 40 41 var err error 42 43 if err = json.Unmarshal(data, &aux); err != nil { 44 return err 45 } 46 47 // This format comes from time.Time.String() source 48 c.ExpiresOn, err = time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", aux.ExpiresOn) 49 50 if err != nil { 51 c.ExpiresOn, err = time.Parse(time.RFC3339, aux.ExpiresOn) 52 } 53 54 if err != nil { 55 return err 56 } 57 58 return nil 59} 60 61// OriginCACertificateListOptions represents the parameters used to list Cloudflare-issued certificates. 62type OriginCACertificateListOptions struct { 63 ZoneID string 64} 65 66// OriginCACertificateID represents the ID of the revoked certificate from the Revoke Certificate endpoint. 67type OriginCACertificateID struct { 68 ID string `json:"id"` 69} 70 71// originCACertificateResponse represents the response from the Create Certificate and the Certificate Details endpoints. 72type originCACertificateResponse struct { 73 Response 74 Result OriginCACertificate `json:"result"` 75} 76 77// originCACertificateResponseList represents the response from the List Certificates endpoint. 78type originCACertificateResponseList struct { 79 Response 80 Result []OriginCACertificate `json:"result"` 81 ResultInfo ResultInfo `json:"result_info"` 82} 83 84// originCACertificateResponseRevoke represents the response from the Revoke Certificate endpoint. 85type originCACertificateResponseRevoke struct { 86 Response 87 Result OriginCACertificateID `json:"result"` 88} 89 90// CreateOriginCertificate creates a Cloudflare-signed certificate. 91// 92// This function requires api.APIUserServiceKey be set to your Certificates API key. 93// 94// API reference: https://api.cloudflare.com/#cloudflare-ca-create-certificate 95func (api *API) CreateOriginCertificate(ctx context.Context, certificate OriginCACertificate) (*OriginCACertificate, error) { 96 uri := "/certificates" 97 res, err := api.makeRequestWithAuthType(ctx, http.MethodPost, uri, certificate, AuthUserService) 98 99 if err != nil { 100 return nil, err 101 } 102 103 var originResponse *originCACertificateResponse 104 105 err = json.Unmarshal(res, &originResponse) 106 107 if err != nil { 108 return nil, errors.Wrap(err, errUnmarshalError) 109 } 110 111 if !originResponse.Success { 112 return nil, errors.New(errRequestNotSuccessful) 113 } 114 115 return &originResponse.Result, nil 116} 117 118// OriginCertificates lists all Cloudflare-issued certificates. 119// 120// This function requires api.APIUserServiceKey be set to your Certificates API key. 121// 122// API reference: https://api.cloudflare.com/#cloudflare-ca-list-certificates 123func (api *API) OriginCertificates(ctx context.Context, options OriginCACertificateListOptions) ([]OriginCACertificate, error) { 124 v := url.Values{} 125 if options.ZoneID != "" { 126 v.Set("zone_id", options.ZoneID) 127 } 128 uri := fmt.Sprintf("/certificates?%s", v.Encode()) 129 res, err := api.makeRequestWithAuthType(ctx, http.MethodGet, uri, nil, AuthUserService) 130 131 if err != nil { 132 return nil, err 133 } 134 135 var originResponse *originCACertificateResponseList 136 137 err = json.Unmarshal(res, &originResponse) 138 139 if err != nil { 140 return nil, errors.Wrap(err, errUnmarshalError) 141 } 142 143 if !originResponse.Success { 144 return nil, errors.New(errRequestNotSuccessful) 145 } 146 147 return originResponse.Result, nil 148} 149 150// OriginCertificate returns the details for a Cloudflare-issued certificate. 151// 152// This function requires api.APIUserServiceKey be set to your Certificates API key. 153// 154// API reference: https://api.cloudflare.com/#cloudflare-ca-certificate-details 155func (api *API) OriginCertificate(ctx context.Context, certificateID string) (*OriginCACertificate, error) { 156 uri := fmt.Sprintf("/certificates/%s", certificateID) 157 res, err := api.makeRequestWithAuthType(ctx, http.MethodGet, uri, nil, AuthUserService) 158 159 if err != nil { 160 return nil, err 161 } 162 163 var originResponse *originCACertificateResponse 164 165 err = json.Unmarshal(res, &originResponse) 166 167 if err != nil { 168 return nil, errors.Wrap(err, errUnmarshalError) 169 } 170 171 if !originResponse.Success { 172 return nil, errors.New(errRequestNotSuccessful) 173 } 174 175 return &originResponse.Result, nil 176} 177 178// RevokeOriginCertificate revokes a created certificate for a zone. 179// 180// This function requires api.APIUserServiceKey be set to your Certificates API key. 181// 182// API reference: https://api.cloudflare.com/#cloudflare-ca-revoke-certificate 183func (api *API) RevokeOriginCertificate(ctx context.Context, certificateID string) (*OriginCACertificateID, error) { 184 uri := fmt.Sprintf("/certificates/%s", certificateID) 185 res, err := api.makeRequestWithAuthType(ctx, http.MethodDelete, uri, nil, AuthUserService) 186 187 if err != nil { 188 return nil, err 189 } 190 191 var originResponse *originCACertificateResponseRevoke 192 193 err = json.Unmarshal(res, &originResponse) 194 195 if err != nil { 196 return nil, errors.Wrap(err, errUnmarshalError) 197 } 198 199 if !originResponse.Success { 200 return nil, errors.New(errRequestNotSuccessful) 201 } 202 203 return &originResponse.Result, nil 204} 205 206// Gets the Cloudflare Origin CA Root Certificate for a given algorithm in PEM format. 207// Algorithm must be one of ['ecc', 'rsa']. 208func OriginCARootCertificate(algorithm string) ([]byte, error) { 209 var url string 210 switch algorithm { 211 case "ecc": 212 url = originCARootCertEccURL 213 case "rsa": 214 url = originCARootCertRsaURL 215 default: 216 return nil, fmt.Errorf("invalid algorithm: must be one of ['ecc', 'rsa']") 217 } 218 219 resp, err := http.Get(url) 220 if err != nil { 221 return nil, errors.Wrap(err, "HTTP request failed") 222 } 223 defer resp.Body.Close() 224 body, err := ioutil.ReadAll(resp.Body) 225 if err != nil { 226 return nil, errors.Wrap(err, "Response body could not be read") 227 } 228 229 return body, nil 230} 231