github.com/akamai/AkamaiOPEN-edgegrid-golang/v5@v5.0.0/pkg/dns/tsig.go (about) 1 package dns 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 8 validation "github.com/go-ozzo/ozzo-validation/v4" 9 10 "reflect" 11 "strings" 12 "sync" 13 ) 14 15 var ( 16 tsigWriteLock sync.Mutex 17 ) 18 19 type ( 20 // TSIGKeys contains operations available on TSIKeyG resource. 21 TSIGKeys interface { 22 // NewTsigKey returns bare bones tsig key struct. 23 NewTsigKey(context.Context, string) *TSIGKey 24 // NewTsigQueryString returns empty query string struct. No elements required. 25 NewTsigQueryString(context.Context) *TSIGQueryString 26 // ListTsigKeys lists the TSIG keys used by zones that you are allowed to manage. 27 // 28 // See: https://techdocs.akamai.com/edge-dns/reference/get-keys 29 ListTsigKeys(context.Context, *TSIGQueryString) (*TSIGReportResponse, error) 30 // GetTsigKeyZones retrieves DNS Zones using tsig key. 31 // 32 // See: https://techdocs.akamai.com/edge-dns/reference/post-keys-used-by 33 GetTsigKeyZones(context.Context, *TSIGKey) (*ZoneNameListResponse, error) 34 // GetTsigKeyAliases retrieves a DNS Zone's aliases. 35 // 36 // See: https://techdocs.akamai.com/edge-dns/reference/get-zones-zone-key-used-by 37 GetTsigKeyAliases(context.Context, string) (*ZoneNameListResponse, error) 38 // TsigKeyBulkUpdate updates Bulk Zones tsig key. 39 // 40 // See: https://techdocs.akamai.com/edge-dns/reference/post-keys-bulk-update 41 TsigKeyBulkUpdate(context.Context, *TSIGKeyBulkPost) error 42 // GetTsigKey retrieves a Tsig key for zone. 43 // 44 // See: https://techdocs.akamai.com/edge-dns/reference/get-zones-zone-key 45 GetTsigKey(context.Context, string) (*TSIGKeyResponse, error) 46 // DeleteTsigKey deletes tsig key for zone. 47 // 48 // See: https://techdocs.akamai.com/edge-dns/reference/delete-zones-zone-key 49 DeleteTsigKey(context.Context, string) error 50 // UpdateTsigKey updates tsig key for zone. 51 // 52 // See: https://techdocs.akamai.com/edge-dns/reference/put-zones-zone-key 53 UpdateTsigKey(context.Context, *TSIGKey, string) error 54 } 55 56 // TSIGQueryString contains TSIG query parameters 57 TSIGQueryString struct { 58 ContractIds []string `json:"contractIds,omitempty"` 59 Search string `json:"search,omitempty"` 60 SortBy []string `json:"sortBy,omitempty"` 61 Gid int64 `json:"gid,omitempty"` 62 } 63 64 // TSIGKey contains TSIG key POST response 65 TSIGKey struct { 66 Name string `json:"name"` 67 Algorithm string `json:"algorithm,omitempty"` 68 Secret string `json:"secret,omitempty"` 69 } 70 // TSIGKeyResponse contains TSIG key GET response 71 TSIGKeyResponse struct { 72 TSIGKey 73 ZoneCount int64 `json:"zonesCount,omitempty"` 74 } 75 76 // TSIGKeyBulkPost contains TSIG key and a list of names of zones that should use the key. Used with update function. 77 TSIGKeyBulkPost struct { 78 Key *TSIGKey `json:"key"` 79 Zones []string `json:"zones"` 80 } 81 82 // TSIGZoneAliases contains list of zone aliases 83 TSIGZoneAliases struct { 84 Aliases []string `json:"aliases"` 85 } 86 87 // TSIGReportMeta contains metadata for TSIGReport response 88 TSIGReportMeta struct { 89 TotalElements int64 `json:"totalElements"` 90 Search string `json:"search,omitempty"` 91 Contracts []string `json:"contracts,omitempty"` 92 Gid int64 `json:"gid,omitempty"` 93 SortBy []string `json:"sortBy,omitempty"` 94 } 95 96 // TSIGReportResponse contains response with a list of the TSIG keys used by zones. 97 TSIGReportResponse struct { 98 Metadata *TSIGReportMeta `json:"metadata"` 99 Keys []*TSIGKeyResponse `json:"keys,omitempty"` 100 } 101 ) 102 103 // Validate validates RecordBody 104 func (key *TSIGKey) Validate() error { 105 106 return validation.Errors{ 107 "Name": validation.Validate(key.Name, validation.Required), 108 "Algorithm": validation.Validate(key.Algorithm, validation.Required), 109 "Secret": validation.Validate(key.Secret, validation.Required), 110 }.Filter() 111 } 112 113 // Validate validates TSIGKeyBulkPost 114 func (bulk *TSIGKeyBulkPost) Validate() error { 115 return validation.Errors{ 116 "Key": validation.Validate(bulk.Key, validation.Required), 117 "Zones": validation.Validate(bulk.Zones, validation.Required), 118 }.Filter() 119 } 120 121 func (p *dns) NewTsigKey(ctx context.Context, name string) *TSIGKey { 122 123 logger := p.Log(ctx) 124 logger.Debug("NewTsigKey") 125 126 key := &TSIGKey{Name: name} 127 return key 128 } 129 130 func (p *dns) NewTsigQueryString(ctx context.Context) *TSIGQueryString { 131 132 logger := p.Log(ctx) 133 logger.Debug("NewTsigQueryString") 134 135 tsigquerystring := &TSIGQueryString{} 136 return tsigquerystring 137 } 138 139 func constructTsigQueryString(tsigquerystring *TSIGQueryString) string { 140 141 queryString := "" 142 qsElems := reflect.ValueOf(tsigquerystring).Elem() 143 for i := 0; i < qsElems.NumField(); i++ { 144 varName := qsElems.Type().Field(i).Name 145 varValue := qsElems.Field(i).Interface() 146 keyVal := fmt.Sprint(varValue) 147 switch varName { 148 case "ContractIds": 149 contractList := "" 150 for j, id := range varValue.([]string) { 151 contractList += id 152 if j < len(varValue.([]string))-1 { 153 contractList += "%2C" 154 } 155 } 156 if len(varValue.([]string)) > 0 { 157 queryString += "contractIds=" + contractList 158 } 159 case "SortBy": 160 sortByList := "" 161 for j, sb := range varValue.([]string) { 162 sortByList += sb 163 if j < len(varValue.([]string))-1 { 164 sortByList += "%2C" 165 } 166 } 167 if len(varValue.([]string)) > 0 { 168 queryString += "sortBy=" + sortByList 169 } 170 case "Search": 171 if keyVal != "" { 172 queryString += "search=" + keyVal 173 } 174 case "Gid": 175 if varValue.(int64) != 0 { 176 queryString += "gid=" + keyVal 177 } 178 } 179 if i < qsElems.NumField()-1 { 180 queryString += "&" 181 } 182 } 183 queryString = strings.TrimRight(queryString, "&") 184 if len(queryString) > 0 { 185 return "?" + queryString 186 } 187 return "" 188 } 189 190 func (p *dns) ListTsigKeys(ctx context.Context, tsigquerystring *TSIGQueryString) (*TSIGReportResponse, error) { 191 192 logger := p.Log(ctx) 193 logger.Debug("ListTsigKeys") 194 195 var tsigList TSIGReportResponse 196 getURL := fmt.Sprintf("/config-dns/v2/keys%s", constructTsigQueryString(tsigquerystring)) 197 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 198 if err != nil { 199 return nil, fmt.Errorf("failed to create ListTsigKeyss request: %w", err) 200 } 201 202 resp, err := p.Exec(req, &tsigList) 203 if err != nil { 204 return nil, fmt.Errorf(" ListTsigKeys request failed: %w", err) 205 } 206 207 if resp.StatusCode != http.StatusOK { 208 return nil, p.Error(resp) 209 } 210 211 return &tsigList, nil 212 213 } 214 215 func (p *dns) GetTsigKeyZones(ctx context.Context, tsigKey *TSIGKey) (*ZoneNameListResponse, error) { 216 217 logger := p.Log(ctx) 218 logger.Debug("GetTsigKeyZones") 219 220 if err := tsigKey.Validate(); err != nil { 221 return nil, err 222 } 223 224 reqbody, err := convertStructToReqBody(tsigKey) 225 if err != nil { 226 return nil, fmt.Errorf("failed to generate request body: %w", err) 227 } 228 229 var zonesList ZoneNameListResponse 230 postURL := "/config-dns/v2/keys/used-by" 231 req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, reqbody) 232 if err != nil { 233 return nil, fmt.Errorf("failed to create GetTsigKeyZones request: %w", err) 234 } 235 236 resp, err := p.Exec(req, &zonesList) 237 if err != nil { 238 return nil, fmt.Errorf("GetTsigKeyZones request failed: %w", err) 239 } 240 241 if resp.StatusCode != http.StatusOK { 242 return nil, p.Error(resp) 243 } 244 245 return &zonesList, nil 246 } 247 248 // TODO: Reconcile 249 func (p *dns) GetTsigKeyAliases(ctx context.Context, zone string) (*ZoneNameListResponse, error) { 250 251 logger := p.Log(ctx) 252 logger.Debug("GetTsigKeyAliases") 253 254 var zonesList ZoneNameListResponse 255 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/key/used-by", zone) 256 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 257 if err != nil { 258 return nil, fmt.Errorf("failed to create GetTsigKeyAliases request: %w", err) 259 } 260 261 resp, err := p.Exec(req, &zonesList) 262 if err != nil { 263 return nil, fmt.Errorf("GetTsigKeyAliases request failed: %w", err) 264 } 265 266 if resp.StatusCode != http.StatusOK { 267 return nil, p.Error(resp) 268 } 269 270 return &zonesList, nil 271 } 272 273 func (p *dns) TsigKeyBulkUpdate(ctx context.Context, tsigBulk *TSIGKeyBulkPost) error { 274 275 logger := p.Log(ctx) 276 logger.Debug("TsigKeyBulkUpdate") 277 278 if err := tsigBulk.Validate(); err != nil { 279 return err 280 } 281 282 reqbody, err := convertStructToReqBody(tsigBulk) 283 if err != nil { 284 return fmt.Errorf("failed to generate request body: %w", err) 285 } 286 287 postURL := "/config-dns/v2/keys/bulk-update" 288 req, err := http.NewRequestWithContext(ctx, http.MethodPost, postURL, reqbody) 289 if err != nil { 290 return fmt.Errorf("failed to create TsigKeyBulkUpdate request: %w", err) 291 } 292 293 resp, err := p.Exec(req, nil) 294 if err != nil { 295 return fmt.Errorf("TsigKeyBulkUpdate request failed: %w", err) 296 } 297 298 if resp.StatusCode != http.StatusNoContent { 299 return p.Error(resp) 300 } 301 302 return nil 303 } 304 305 func (p *dns) GetTsigKey(ctx context.Context, zone string) (*TSIGKeyResponse, error) { 306 307 logger := p.Log(ctx) 308 logger.Debug("GetTsigKey") 309 310 var zonekey TSIGKeyResponse 311 getURL := fmt.Sprintf("/config-dns/v2/zones/%s/key", zone) 312 req, err := http.NewRequestWithContext(ctx, http.MethodGet, getURL, nil) 313 if err != nil { 314 return nil, fmt.Errorf("failed to create GetTsigKey request: %w", err) 315 } 316 317 resp, err := p.Exec(req, &zonekey) 318 if err != nil { 319 return nil, fmt.Errorf("GetTsigKey request failed: %w", err) 320 } 321 322 if resp.StatusCode != http.StatusOK { 323 return nil, p.Error(resp) 324 } 325 326 return &zonekey, nil 327 } 328 329 func (p *dns) DeleteTsigKey(ctx context.Context, zone string) error { 330 331 logger := p.Log(ctx) 332 logger.Debug("DeleteTsigKey") 333 334 delURL := fmt.Sprintf("/config-dns/v2/zones/%s/key", zone) 335 req, err := http.NewRequestWithContext(ctx, http.MethodDelete, delURL, nil) 336 if err != nil { 337 return fmt.Errorf("failed to create DeleteTsigKey request: %w", err) 338 } 339 340 resp, err := p.Exec(req, nil) 341 if err != nil { 342 return fmt.Errorf("DeleteTsigKey request failed: %w", err) 343 } 344 345 if resp.StatusCode != http.StatusNoContent { 346 return p.Error(resp) 347 } 348 349 return nil 350 } 351 352 func (p *dns) UpdateTsigKey(ctx context.Context, tsigKey *TSIGKey, zone string) error { 353 354 logger := p.Log(ctx) 355 logger.Debug("UpdateTsigKey") 356 357 if err := tsigKey.Validate(); err != nil { 358 return err 359 } 360 361 reqbody, err := convertStructToReqBody(tsigKey) 362 if err != nil { 363 return fmt.Errorf("failed to generate request body: %w", err) 364 } 365 366 putURL := fmt.Sprintf("/config-dns/v2/zones/%s/key", zone) 367 req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, reqbody) 368 if err != nil { 369 return fmt.Errorf("failed to create UpdateTsigKey request: %w", err) 370 } 371 372 resp, err := p.Exec(req, nil) 373 if err != nil { 374 return fmt.Errorf("UpdateTsigKey request failed: %w", err) 375 } 376 377 if resp.StatusCode != http.StatusNoContent { 378 return p.Error(resp) 379 } 380 381 return nil 382 }