github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/cps/dv_challenges.go (about) 1 package cps 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "net/http" 8 "net/url" 9 ) 10 11 type ( 12 // DVChallenges is a CPS DV challenges API interface 13 DVChallenges interface { 14 // GetChangeLetsEncryptChallenges gets detailed information about Domain Validation challenges 15 // 16 // See: https://techdocs.akamai.com/cps/reference/get-change-allowed-input-param 17 GetChangeLetsEncryptChallenges(context.Context, GetChangeRequest) (*DVArray, error) 18 19 // AcknowledgeDVChallenges sends acknowledgement request to CPS informing that the validation is completed 20 // 21 // See: https://techdocs.akamai.com/cps/reference/post-change-allowed-input-param 22 AcknowledgeDVChallenges(context.Context, AcknowledgementRequest) error 23 } 24 25 // DVArray is an array of DV objects 26 DVArray struct { 27 DV []DV `json:"dv"` 28 } 29 30 // DV is a Domain Validation entity 31 DV struct { 32 Challenges []Challenge `json:"challenges"` 33 Domain string `json:"domain"` 34 Error string `json:"error"` 35 Expires string `json:"expires"` 36 RequestTimestamp string `json:"requestTimestamp"` 37 Status string `json:"status"` 38 ValidatedTimestamp string `json:"validatedTimestamp"` 39 ValidationStatus string `json:"validationStatus"` 40 } 41 42 // Challenge contains domain information of a specific domain to be validated 43 Challenge struct { 44 Error string `json:"error"` 45 FullPath string `json:"fullPath"` 46 RedirectFullPath string `json:"redirectFullPath"` 47 ResponseBody string `json:"responseBody"` 48 Status string `json:"status"` 49 Token string `json:"token"` 50 Type string `json:"type"` 51 ValidationRecords []ValidationRecord `json:"validationRecords"` 52 } 53 54 // ValidationRecord represents validation attempt 55 ValidationRecord struct { 56 Authorities []string `json:"authorities"` 57 Hostname string `json:"hostname"` 58 Port string `json:"port"` 59 ResolvedIP []string `json:"resolvedIp"` 60 TriedIP string `json:"triedIp"` 61 URL string `json:"url"` 62 UsedIP string `json:"usedIp"` 63 } 64 ) 65 66 var ( 67 // ErrGetChangeLetsEncryptChallenges is returned when GetChangeLetsEncryptChallenges fails 68 ErrGetChangeLetsEncryptChallenges = errors.New("fetching change for lets-encrypt-challenges") 69 // ErrAcknowledgeLetsEncryptChallenges when AcknowledgeDVChallenges fails 70 ErrAcknowledgeLetsEncryptChallenges = errors.New("acknowledging lets-encrypt-challenges") 71 ) 72 73 func (c *cps) GetChangeLetsEncryptChallenges(ctx context.Context, params GetChangeRequest) (*DVArray, error) { 74 if err := params.Validate(); err != nil { 75 return nil, fmt.Errorf("%s: %w: %s", ErrGetChangeLetsEncryptChallenges, ErrStructValidation, err) 76 } 77 78 var rval DVArray 79 80 logger := c.Log(ctx) 81 logger.Debug("GetChangeLetsEncryptChallenges") 82 83 uri, err := url.Parse(fmt.Sprintf( 84 "/cps/v2/enrollments/%d/changes/%d/input/info/lets-encrypt-challenges", 85 params.EnrollmentID, 86 params.ChangeID), 87 ) 88 if err != nil { 89 return nil, fmt.Errorf("%w: failed to parse url: %s", ErrGetChangeLetsEncryptChallenges, err) 90 } 91 92 req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri.String(), nil) 93 if err != nil { 94 return nil, fmt.Errorf("%w: failed to create request: %s", ErrGetChangeLetsEncryptChallenges, err) 95 } 96 req.Header.Set("Accept", "application/vnd.akamai.cps.dv-challenges.v2+json") 97 98 resp, err := c.Exec(req, &rval) 99 if err != nil { 100 return nil, fmt.Errorf("%w: request failed: %s", ErrGetChangeLetsEncryptChallenges, err) 101 } 102 103 if resp.StatusCode != http.StatusOK { 104 return nil, fmt.Errorf("%s: %w", ErrGetChangeLetsEncryptChallenges, c.Error(resp)) 105 } 106 107 return &rval, nil 108 } 109 110 func (c *cps) AcknowledgeDVChallenges(ctx context.Context, params AcknowledgementRequest) error { 111 if err := params.Validate(); err != nil { 112 return fmt.Errorf("%s: %w: %s", ErrAcknowledgeLetsEncryptChallenges, ErrStructValidation, err) 113 } 114 115 logger := c.Log(ctx) 116 logger.Debug("AcknowledgeDVVhallenges") 117 118 uri, err := url.Parse(fmt.Sprintf( 119 "/cps/v2/enrollments/%d/changes/%d/input/update/lets-encrypt-challenges-completed", 120 params.EnrollmentID, params.ChangeID)) 121 if err != nil { 122 return fmt.Errorf("%w: parsing URL: %s", ErrAcknowledgeLetsEncryptChallenges, err) 123 } 124 125 req, err := http.NewRequestWithContext(ctx, http.MethodPost, uri.String(), nil) 126 if err != nil { 127 return fmt.Errorf("%w: failed to create request: %s", ErrAcknowledgeLetsEncryptChallenges, err) 128 } 129 req.Header.Set("Accept", "application/vnd.akamai.cps.change-id.v1+json") 130 req.Header.Set("Content-Type", "application/vnd.akamai.cps.acknowledgement.v1+json; charset=utf-8") 131 132 resp, err := c.Exec(req, nil, params.Acknowledgement) 133 if err != nil { 134 return fmt.Errorf("%w: request failed: %s", ErrAcknowledgeLetsEncryptChallenges, err) 135 } 136 137 if resp.StatusCode != http.StatusNoContent && resp.StatusCode != http.StatusAccepted && resp.StatusCode != http.StatusOK { 138 return fmt.Errorf("%s: %w", ErrAcknowledgeLetsEncryptChallenges, c.Error(resp)) 139 } 140 141 return nil 142 }