tsig.go raw
1 package dns
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "net/http"
8 "reflect"
9 "strings"
10
11 "github.com/akamai/AkamaiOPEN-edgegrid-golang/v11/pkg/edgegriderr"
12 "github.com/akamai/AkamaiOPEN-edgegrid-golang/v11/pkg/session"
13 validation "github.com/go-ozzo/ozzo-validation/v4"
14 )
15
16 type (
17 // TSIGQueryString contains TSIG query parameters
18 TSIGQueryString struct {
19 ContractIDs []string `json:"contractIds,omitempty"`
20 Search string `json:"search,omitempty"`
21 SortBy []string `json:"sortBy,omitempty"`
22 GID int64 `json:"gid,omitempty"`
23 }
24
25 // TSIGKey contains TSIG key POST response
26 TSIGKey struct {
27 Name string `json:"name"`
28 Algorithm string `json:"algorithm,omitempty"`
29 Secret string `json:"secret,omitempty"`
30 }
31
32 // TSIGKeyRequest contains request parameter
33 TSIGKeyRequest struct {
34 Zone string
35 }
36
37 // GetTSIGKeyRequest contains request parameters for GetTSIGKey
38 GetTSIGKeyRequest TSIGKeyRequest
39
40 // GetTSIGKeyResponse contains the response data from GetTSIGKey operation
41 GetTSIGKeyResponse struct {
42 TSIGKey
43 ZoneCount int64 `json:"zonesCount,omitempty"`
44 }
45
46 // DeleteTSIGKeyRequest contains request parameters for DeleteTSIGKey
47 DeleteTSIGKeyRequest TSIGKeyRequest
48
49 // GetTSIGKeyAliasesRequest contains request parameters for GetTSIGKeyAliases
50 GetTSIGKeyAliasesRequest TSIGKeyRequest
51
52 // GetTSIGKeyAliasesResponse contains the response data from GetTSIGKeyAliases operation
53 GetTSIGKeyAliasesResponse struct {
54 Zones []string `json:"zones"`
55 Aliases []string `json:"aliases"`
56 }
57
58 // TSIGKeyResponse contains TSIG key GET response
59 TSIGKeyResponse struct {
60 TSIGKey
61 ZoneCount int64 `json:"zonesCount,omitempty"`
62 }
63
64 // TSIGKeyBulkPost contains TSIG key and a list of names of zones that should use the key. Used with update function.
65 TSIGKeyBulkPost struct {
66 Key *TSIGKey `json:"key"`
67 Zones []string `json:"zones"`
68 }
69
70 // TSIGZoneAliases contains list of zone aliases
71 TSIGZoneAliases struct {
72 Aliases []string `json:"aliases"`
73 }
74
75 // TSIGReportMeta contains metadata for TSIGReport response
76 TSIGReportMeta struct {
77 TotalElements int64 `json:"totalElements"`
78 Search string `json:"search,omitempty"`
79 Contracts []string `json:"contracts,omitempty"`
80 GID int64 `json:"gid,omitempty"`
81 SortBy []string `json:"sortBy,omitempty"`
82 }
83
84 // TSIGReportResponse contains response with a list of the TSIG keys used by zones.
85 TSIGReportResponse struct {
86 Metadata *TSIGReportMeta `json:"metadata"`
87 Keys []TSIGKeyResponse `json:"keys,omitempty"`
88 }
89
90 // UpdateTSIGKeyRequest contains request parameters for UpdateTSIGKey
91 UpdateTSIGKeyRequest struct {
92 TsigKey *TSIGKey
93 Zone string
94 }
95
96 // UpdateTSIGKeyBulkRequest contains request parameters for UpdateTSIGKeyBulk
97 UpdateTSIGKeyBulkRequest struct {
98 TSIGKeyBulk *TSIGKeyBulkPost
99 }
100
101 // GetTSIGKeyZonesRequest contains request parameters for GetTSIGKeyZones
102 GetTSIGKeyZonesRequest struct {
103 TsigKey *TSIGKey
104 }
105
106 // GetTSIGKeyZonesResponse contains the response data from GetTSIGKeyZones operation
107 GetTSIGKeyZonesResponse struct {
108 Zones []string `json:"zones"`
109 Aliases []string `json:"aliases"`
110 }
111
112 // ListTSIGKeysRequest contains request parameters for ListTSIGKeys
113 ListTSIGKeysRequest struct {
114 TsigQuery *TSIGQueryString
115 }
116
117 // ListTSIGKeysResponse contains the response data from ListTSIGKeys operation
118 ListTSIGKeysResponse struct {
119 Metadata *TSIGReportMeta `json:"metadata"`
120 Keys []TSIGKeyResponse `json:"keys,omitempty"`
121 }
122 )
123
124 var (
125 // ErrGetTSIGKey is returned when GetTSIGKey fails
126 ErrGetTSIGKey = errors.New("get tsig key")
127 // ErrDeleteTSIGKey is returned when DeleteTSIGKey fails
128 ErrDeleteTSIGKey = errors.New("delete tsig key")
129 // ErrGetTSIGKeyAliases is returned when GetTSIGKeyAliases fails
130 ErrGetTSIGKeyAliases = errors.New("get tsig key aliases")
131 // ErrUpdateTSIGKey is returned when UpdateTSIGKey fails
132 ErrUpdateTSIGKey = errors.New("updated tsig key")
133 // ErrUpdateTSIGKeyBulk is returned when UpdateTSIGKeyBulk fails
134 ErrUpdateTSIGKeyBulk = errors.New("update tsig key for multiple zones")
135 // ErrGetTSIGKeyZones is returned when GetTSIGKeyZones fails
136 ErrGetTSIGKeyZones = errors.New("list zones using tsig key")
137 // ErrListTSIGKeys is returned when ListTSIGKeys fails
138 ErrListTSIGKeys = errors.New("get a list of the tsig keys")
139 )
140
141 // Validate validates GetTSIGKeyRequest
142 func (r GetTSIGKeyRequest) Validate() error {
143 return edgegriderr.ParseValidationErrors(validation.Errors{
144 "Zone": validation.Validate(r.Zone, validation.Required),
145 })
146 }
147
148 // Validate validates DeleteTSIGKeyRequest
149 func (r DeleteTSIGKeyRequest) Validate() error {
150 return edgegriderr.ParseValidationErrors(validation.Errors{
151 "Zone": validation.Validate(r.Zone, validation.Required),
152 })
153 }
154
155 // Validate validates GetTSIGKeyAliasesRequest
156 func (r GetTSIGKeyAliasesRequest) Validate() error {
157 return edgegriderr.ParseValidationErrors(validation.Errors{
158 "Zone": validation.Validate(r.Zone, validation.Required),
159 })
160 }
161
162 // Validate validates UpdateTSIGKeyRequest
163 func (r UpdateTSIGKeyRequest) Validate() error {
164 return edgegriderr.ParseValidationErrors(validation.Errors{
165 "Zone": validation.Validate(r.Zone, validation.Required),
166 "TsigKey": validation.Validate(r.TsigKey),
167 })
168 }
169
170 // Validate validates UpdateTSIGKeyBulkRequest
171 func (r UpdateTSIGKeyBulkRequest) Validate() error {
172 return edgegriderr.ParseValidationErrors(validation.Errors{
173 "TSIGKeyBulk": validation.Validate(r.TSIGKeyBulk, validation.Required),
174 })
175 }
176
177 // Validate validates GetTSIGKeyZonesRequest
178 func (r GetTSIGKeyZonesRequest) Validate() error {
179 return edgegriderr.ParseValidationErrors(validation.Errors{
180 "TsigKey": validation.Validate(r.TsigKey, validation.Required),
181 })
182 }
183
184 // Validate validates TSIGKey
185 func (key *TSIGKey) Validate() error {
186 return validation.Errors{
187 "Name": validation.Validate(key.Name, validation.Required),
188 "Algorithm": validation.Validate(key.Algorithm, validation.Required),
189 "Secret": validation.Validate(key.Secret, validation.Required),
190 }.Filter()
191 }
192
193 // Validate validates TSIGKeyBulkPost
194 func (bulk *TSIGKeyBulkPost) Validate() error {
195 return validation.Errors{
196 "Key": validation.Validate(bulk.Key, validation.Required),
197 "Zones": validation.Validate(bulk.Zones, validation.Required),
198 }.Filter()
199 }
200
201 func constructTSIGQueryString(tsigQueryString *TSIGQueryString) string {
202 queryString := ""
203 qsElems := reflect.ValueOf(tsigQueryString).Elem()
204 for i := 0; i < qsElems.NumField(); i++ {
205 varName := qsElems.Type().Field(i).Name
206 varValue := qsElems.Field(i).Interface()
207 keyVal := fmt.Sprint(varValue)
208 switch varName {
209 case "ContractIDs":
210 contractList := ""
211 for j, id := range varValue.([]string) {
212 contractList += id
213 if j < len(varValue.([]string))-1 {
214 contractList += "%2C"
215 }
216 }
217 if len(varValue.([]string)) > 0 {
218 queryString += "contractIds=" + contractList
219 }
220 case "SortBy":
221 sortByList := ""
222 for j, sb := range varValue.([]string) {
223 sortByList += sb
224 if j < len(varValue.([]string))-1 {
225 sortByList += "%2C"
226 }
227 }
228 if len(varValue.([]string)) > 0 {
229 queryString += "sortBy=" + sortByList
230 }
231 case "Search":
232 if keyVal != "" {
233 queryString += "search=" + keyVal
234 }
235 case "GID":
236 if varValue.(int64) != 0 {
237 queryString += "gid=" + keyVal
238 }
239 }
240 if i < qsElems.NumField()-1 {
241 queryString += "&"
242 }
243 }
244 queryString = strings.TrimRight(queryString, "&")
245 if len(queryString) > 0 {
246 return "?" + queryString
247 }
248 return ""
249 }
250
251 func (d *dns) ListTSIGKeys(ctx context.Context, params ListTSIGKeysRequest) (*ListTSIGKeysResponse, error) {
252 logger := d.Log(ctx)
253 logger.Debug("ListTSIGKeys")
254
255 getURL := fmt.Sprintf("/config-dns/v2/keys%s", constructTSIGQueryString(params.TsigQuery))
256 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
257 if err != nil {
258 return nil, fmt.Errorf("failed to create ListTsigKeyss request: %w", err)
259 }
260
261 var result ListTSIGKeysResponse
262 resp, err := d.Exec(req, &result)
263 if err != nil {
264 return nil, fmt.Errorf(" ListTsigKeys request failed: %w", err)
265 }
266 defer session.CloseResponseBody(resp)
267
268 if resp.StatusCode != http.StatusOK {
269 return nil, d.Error(resp)
270 }
271
272 return &result, nil
273 }
274
275 func (d *dns) GetTSIGKeyZones(ctx context.Context, params GetTSIGKeyZonesRequest) (*GetTSIGKeyZonesResponse, error) {
276 logger := d.Log(ctx)
277 logger.Debug("GetTSIGKeyZones")
278
279 if err := params.Validate(); err != nil {
280 return nil, fmt.Errorf("%s: %w: %s", ErrGetTSIGKeyZones, ErrStructValidation, err)
281 }
282
283 reqBody, err := convertStructToReqBody(params.TsigKey)
284 if err != nil {
285 return nil, fmt.Errorf("failed to generate request body: %w", err)
286 }
287
288 postURL := "/config-dns/v2/keys/used-by"
289 req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, reqBody)
290 if err != nil {
291 return nil, fmt.Errorf("failed to create GetTsigKeyZones request: %w", err)
292 }
293
294 var result GetTSIGKeyZonesResponse
295 resp, err := d.Exec(req, &result)
296 if err != nil {
297 return nil, fmt.Errorf("GetTsigKeyZones request failed: %w", err)
298 }
299 defer session.CloseResponseBody(resp)
300
301 if resp.StatusCode != http.StatusOK {
302 return nil, d.Error(resp)
303 }
304
305 return &result, nil
306 }
307
308 func (d *dns) GetTSIGKeyAliases(ctx context.Context, params GetTSIGKeyAliasesRequest) (*GetTSIGKeyAliasesResponse, error) {
309 logger := d.Log(ctx)
310 logger.Debug("GetTSIGKeyAliases")
311
312 if err := params.Validate(); err != nil {
313 return nil, fmt.Errorf("%s: %w: %s", ErrGetTSIGKeyAliases, ErrStructValidation, err)
314 }
315
316 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/key/used-by", params.Zone)
317 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
318 if err != nil {
319 return nil, fmt.Errorf("failed to create GetTsigKeyAliases request: %w", err)
320 }
321
322 var result GetTSIGKeyAliasesResponse
323 resp, err := d.Exec(req, &result)
324 if err != nil {
325 return nil, fmt.Errorf("GetTsigKeyAliases request failed: %w", err)
326 }
327 defer session.CloseResponseBody(resp)
328
329 if resp.StatusCode != http.StatusOK {
330 return nil, d.Error(resp)
331 }
332
333 return &result, nil
334 }
335
336 func (d *dns) UpdateTSIGKeyBulk(ctx context.Context, params UpdateTSIGKeyBulkRequest) error {
337 logger := d.Log(ctx)
338 logger.Debug("TSIGKeyBulkUpdate")
339
340 if err := params.Validate(); err != nil {
341 return fmt.Errorf("%s: %w: %s", ErrUpdateTSIGKeyBulk, ErrStructValidation, err)
342 }
343
344 reqBody, err := convertStructToReqBody(params.TSIGKeyBulk)
345 if err != nil {
346 return fmt.Errorf("failed to generate request body: %w", err)
347 }
348
349 postURL := "/config-dns/v2/keys/bulk-update"
350 req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, reqBody)
351 if err != nil {
352 return fmt.Errorf("failed to create TsigKeyBulkUpdate request: %w", err)
353 }
354
355 resp, err := d.Exec(req, nil)
356 if err != nil {
357 return fmt.Errorf("TsigKeyBulkUpdate request failed: %w", err)
358 }
359 defer session.CloseResponseBody(resp)
360
361 if resp.StatusCode != http.StatusNoContent {
362 return d.Error(resp)
363 }
364
365 return nil
366 }
367
368 func (d *dns) GetTSIGKey(ctx context.Context, params GetTSIGKeyRequest) (*GetTSIGKeyResponse, error) {
369 logger := d.Log(ctx)
370 logger.Debug("GetTSIGKey")
371
372 if err := params.Validate(); err != nil {
373 return nil, fmt.Errorf("%s: %w: %s", ErrGetTSIGKey, ErrStructValidation, err)
374 }
375
376 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/key", params.Zone)
377 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil)
378 if err != nil {
379 return nil, fmt.Errorf("failed to create GetTsigKey request: %w", err)
380 }
381
382 var result GetTSIGKeyResponse
383 resp, err := d.Exec(req, &result)
384 if err != nil {
385 return nil, fmt.Errorf("GetTsigKey request failed: %w", err)
386 }
387 defer session.CloseResponseBody(resp)
388
389 if resp.StatusCode != http.StatusOK {
390 return nil, d.Error(resp)
391 }
392
393 return &result, nil
394 }
395
396 func (d *dns) DeleteTSIGKey(ctx context.Context, params DeleteTSIGKeyRequest) error {
397 logger := d.Log(ctx)
398 logger.Debug("DeleteTSIGKey")
399
400 if err := params.Validate(); err != nil {
401 return fmt.Errorf("%s: %w: %s", ErrDeleteTSIGKey, ErrStructValidation, err)
402 }
403
404 delURL := fmt.Sprintf("/config-dns/v2/zones/%s/key", params.Zone)
405 req, err := http.NewRequestWithContext(ctx, http.MethodDelete, delURL, nil)
406 if err != nil {
407 return fmt.Errorf("failed to create DeleteTsigKey request: %w", err)
408 }
409
410 resp, err := d.Exec(req, nil)
411 if err != nil {
412 return fmt.Errorf("DeleteTsigKey request failed: %w", err)
413 }
414 defer session.CloseResponseBody(resp)
415
416 if resp.StatusCode != http.StatusNoContent {
417 return d.Error(resp)
418 }
419
420 return nil
421 }
422
423 func (d *dns) UpdateTSIGKey(ctx context.Context, params UpdateTSIGKeyRequest) error {
424 logger := d.Log(ctx)
425 logger.Debug("UpdateTSIGKey")
426
427 if err := params.Validate(); err != nil {
428 return fmt.Errorf("%s: %w: %s", ErrUpdateTSIGKey, ErrStructValidation, err)
429 }
430
431 reqBody, err := convertStructToReqBody(params.TsigKey)
432 if err != nil {
433 return fmt.Errorf("failed to generate request body: %w", err)
434 }
435
436 putURL := fmt.Sprintf("/config-dns/v2/zones/%s/key", params.Zone)
437 req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, reqBody)
438 if err != nil {
439 return fmt.Errorf("failed to create UpdateTsigKey request: %w", err)
440 }
441
442 resp, err := d.Exec(req, nil)
443 if err != nil {
444 return fmt.Errorf("UpdateTsigKey request failed: %w", err)
445 }
446 defer session.CloseResponseBody(resp)
447
448 if resp.StatusCode != http.StatusNoContent {
449 return d.Error(resp)
450 }
451
452 return nil
453 }
454