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