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  }