github.com/outbrain/consul@v1.4.5/agent/structs/acl.go (about)

     1  package structs
     2  
     3  import (
     4  	"encoding/binary"
     5  	"errors"
     6  	"fmt"
     7  	"hash/fnv"
     8  	"sort"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/hashicorp/consul/acl"
    13  	"github.com/hashicorp/consul/sentinel"
    14  	"golang.org/x/crypto/blake2b"
    15  )
    16  
    17  type ACLMode string
    18  
    19  const (
    20  	// ACLs are disabled by configuration
    21  	ACLModeDisabled ACLMode = "0"
    22  	// ACLs are enabled
    23  	ACLModeEnabled ACLMode = "1"
    24  	// DEPRECATED (ACL-Legacy-Compat) - only needed while legacy ACLs are supported
    25  	// ACLs are enabled and using legacy ACLs
    26  	ACLModeLegacy ACLMode = "2"
    27  	// DEPRECATED (ACL-Legacy-Compat) - only needed while legacy ACLs are supported
    28  	// ACLs are assumed enabled but not being advertised
    29  	ACLModeUnknown ACLMode = "3"
    30  )
    31  
    32  // ACLOp is used in RPCs to encode ACL operations.
    33  type ACLOp string
    34  
    35  type ACLTokenIDType string
    36  
    37  const (
    38  	ACLTokenSecret   ACLTokenIDType = "secret"
    39  	ACLTokenAccessor ACLTokenIDType = "accessor"
    40  )
    41  
    42  type ACLPolicyIDType string
    43  
    44  const (
    45  	ACLPolicyName ACLPolicyIDType = "name"
    46  	ACLPolicyID   ACLPolicyIDType = "id"
    47  )
    48  
    49  const (
    50  	// All policy ids with the first 120 bits set to all zeroes are
    51  	// reserved for builtin policies. Policy creation will ensure we
    52  	// dont accidentally create them when autogenerating uuids.
    53  
    54  	// This policy gives unlimited access to everything. Users
    55  	// may rename if desired but cannot delete or modify the rules.
    56  	ACLPolicyGlobalManagementID = "00000000-0000-0000-0000-000000000001"
    57  	ACLPolicyGlobalManagement   = `
    58  acl = "write"
    59  agent_prefix "" {
    60  	policy = "write"
    61  }
    62  event_prefix "" {
    63  	policy = "write"
    64  }
    65  key_prefix "" {
    66  	policy = "write"
    67  }
    68  keyring = "write"
    69  node_prefix "" {
    70  	policy = "write"
    71  }
    72  operator = "write"
    73  query_prefix "" {
    74  	policy = "write"
    75  }
    76  service_prefix "" {
    77  	policy = "write"
    78  	intentions = "write"
    79  }
    80  session_prefix "" {
    81  	policy = "write"
    82  }`
    83  
    84  	// This is the policy ID for anonymous access. This is configurable by the
    85  	// user.
    86  	ACLTokenAnonymousID = "00000000-0000-0000-0000-000000000002"
    87  )
    88  
    89  func ACLIDReserved(id string) bool {
    90  	return strings.HasPrefix(id, "00000000-0000-0000-0000-0000000000")
    91  }
    92  
    93  const (
    94  	// ACLSet creates or updates a token.
    95  	ACLSet ACLOp = "set"
    96  
    97  	// ACLDelete deletes a token.
    98  	ACLDelete ACLOp = "delete"
    99  )
   100  
   101  // ACLBootstrapNotAllowedErr is returned once we know that a bootstrap can no
   102  // longer be done since the cluster was bootstrapped
   103  var ACLBootstrapNotAllowedErr = errors.New("ACL bootstrap no longer allowed")
   104  
   105  // ACLBootstrapInvalidResetIndexErr is returned when bootstrap is requested with a non-zero
   106  // reset index but the index doesn't match the bootstrap index
   107  var ACLBootstrapInvalidResetIndexErr = errors.New("Invalid ACL bootstrap reset index")
   108  
   109  type ACLIdentity interface {
   110  	// ID returns a string that can be used for logging and telemetry. This should not
   111  	// contain any secret data used for authentication
   112  	ID() string
   113  	SecretToken() string
   114  	PolicyIDs() []string
   115  	EmbeddedPolicy() *ACLPolicy
   116  }
   117  
   118  type ACLTokenPolicyLink struct {
   119  	ID   string
   120  	Name string `hash:"ignore"`
   121  }
   122  
   123  type ACLToken struct {
   124  	// This is the UUID used for tracking and management purposes
   125  	AccessorID string
   126  
   127  	// This is the UUID used as the api token by clients
   128  	SecretID string
   129  
   130  	// Human readable string to display for the token (Optional)
   131  	Description string
   132  
   133  	// List of policy links - nil/empty for legacy tokens
   134  	// Note this is the list of IDs and not the names. Prior to token creation
   135  	// the list of policy names gets validated and the policy IDs get stored herein
   136  	Policies []ACLTokenPolicyLink
   137  
   138  	// Type is the V1 Token Type
   139  	// DEPRECATED (ACL-Legacy-Compat) - remove once we no longer support v1 ACL compat
   140  	// Even though we are going to auto upgrade management tokens we still
   141  	// want to be able to have the old APIs operate on the upgraded management tokens
   142  	// so this field is being kept to identify legacy tokens even after an auto-upgrade
   143  	Type string `json:"-"`
   144  
   145  	// Rules is the V1 acl rules associated with
   146  	// DEPRECATED (ACL-Legacy-Compat) - remove once we no longer support v1 ACL compat
   147  	Rules string `json:",omitempty"`
   148  
   149  	// Whether this token is DC local. This means that it will not be synced
   150  	// to the ACL datacenter and replicated to others.
   151  	Local bool
   152  
   153  	// The time when this token was created
   154  	CreateTime time.Time `json:",omitempty"`
   155  
   156  	// Hash of the contents of the token
   157  	//
   158  	// This is needed mainly for replication purposes. When replicating from
   159  	// one DC to another keeping the content Hash will allow us to avoid
   160  	// unnecessary calls to the authoritative DC
   161  	Hash []byte
   162  
   163  	// Embedded Raft Metadata
   164  	RaftIndex
   165  }
   166  
   167  func (t *ACLToken) Clone() *ACLToken {
   168  	t2 := *t
   169  	t2.Policies = nil
   170  
   171  	if len(t.Policies) > 0 {
   172  		t2.Policies = make([]ACLTokenPolicyLink, len(t.Policies))
   173  		copy(t2.Policies, t.Policies)
   174  	}
   175  	return &t2
   176  }
   177  
   178  func (t *ACLToken) ID() string {
   179  	return t.AccessorID
   180  }
   181  
   182  func (t *ACLToken) SecretToken() string {
   183  	return t.SecretID
   184  }
   185  
   186  func (t *ACLToken) PolicyIDs() []string {
   187  	var ids []string
   188  	for _, link := range t.Policies {
   189  		ids = append(ids, link.ID)
   190  	}
   191  	return ids
   192  }
   193  
   194  func (t *ACLToken) EmbeddedPolicy() *ACLPolicy {
   195  	// DEPRECATED (ACL-Legacy-Compat)
   196  	//
   197  	// For legacy tokens with embedded rules this provides a way to map those
   198  	// rules to an ACLPolicy. This function can just return nil once legacy
   199  	// acl compatibility is no longer needed.
   200  	//
   201  	// Additionally for management tokens we must embed the policy rules
   202  	// as well
   203  	policy := &ACLPolicy{}
   204  	if t.Type == ACLTokenTypeManagement {
   205  		hasher := fnv.New128a()
   206  		policy.ID = fmt.Sprintf("%x", hasher.Sum([]byte(ACLPolicyGlobalManagement)))
   207  		policy.Name = "legacy-management"
   208  		policy.Rules = ACLPolicyGlobalManagement
   209  		policy.Syntax = acl.SyntaxCurrent
   210  	} else if t.Rules != "" || t.Type == ACLTokenTypeClient {
   211  		hasher := fnv.New128a()
   212  		policy.ID = fmt.Sprintf("%x", hasher.Sum([]byte(t.Rules)))
   213  		policy.Name = fmt.Sprintf("legacy-policy-%s", policy.ID)
   214  		policy.Rules = t.Rules
   215  		policy.Syntax = acl.SyntaxLegacy
   216  	} else {
   217  		return nil
   218  	}
   219  
   220  	policy.SetHash(true)
   221  	return policy
   222  }
   223  
   224  func (t *ACLToken) SetHash(force bool) []byte {
   225  	if force || t.Hash == nil {
   226  		// Initialize a 256bit Blake2 hash (32 bytes)
   227  		hash, err := blake2b.New256(nil)
   228  		if err != nil {
   229  			panic(err)
   230  		}
   231  
   232  		// Write all the user set fields
   233  		hash.Write([]byte(t.Description))
   234  		hash.Write([]byte(t.Type))
   235  		hash.Write([]byte(t.Rules))
   236  
   237  		if t.Local {
   238  			hash.Write([]byte("local"))
   239  		} else {
   240  			hash.Write([]byte("global"))
   241  		}
   242  
   243  		for _, link := range t.Policies {
   244  			hash.Write([]byte(link.ID))
   245  		}
   246  
   247  		// Finalize the hash
   248  		hashVal := hash.Sum(nil)
   249  
   250  		// Set and return the hash
   251  		t.Hash = hashVal
   252  	}
   253  	return t.Hash
   254  }
   255  
   256  func (t *ACLToken) EstimateSize() int {
   257  	// 33 = 16 (RaftIndex) + 8 (Hash) + 8 (CreateTime) + 1 (Local)
   258  	size := 33 + len(t.AccessorID) + len(t.SecretID) + len(t.Description) + len(t.Type) + len(t.Rules)
   259  	for _, link := range t.Policies {
   260  		size += len(link.ID) + len(link.Name)
   261  	}
   262  	return size
   263  }
   264  
   265  // ACLTokens is a slice of ACLTokens.
   266  type ACLTokens []*ACLToken
   267  
   268  type ACLTokenListStub struct {
   269  	AccessorID  string
   270  	Description string
   271  	Policies    []ACLTokenPolicyLink
   272  	Local       bool
   273  	CreateTime  time.Time `json:",omitempty"`
   274  	Hash        []byte
   275  	CreateIndex uint64
   276  	ModifyIndex uint64
   277  	Legacy      bool `json:",omitempty"`
   278  }
   279  
   280  type ACLTokenListStubs []*ACLTokenListStub
   281  
   282  func (token *ACLToken) Stub() *ACLTokenListStub {
   283  	return &ACLTokenListStub{
   284  		AccessorID:  token.AccessorID,
   285  		Description: token.Description,
   286  		Policies:    token.Policies,
   287  		Local:       token.Local,
   288  		CreateTime:  token.CreateTime,
   289  		Hash:        token.Hash,
   290  		CreateIndex: token.CreateIndex,
   291  		ModifyIndex: token.ModifyIndex,
   292  		Legacy:      token.Rules != "",
   293  	}
   294  }
   295  
   296  func (tokens ACLTokens) Sort() {
   297  	sort.Slice(tokens, func(i, j int) bool {
   298  		return tokens[i].AccessorID < tokens[j].AccessorID
   299  	})
   300  }
   301  
   302  func (tokens ACLTokenListStubs) Sort() {
   303  	sort.Slice(tokens, func(i, j int) bool {
   304  		return tokens[i].AccessorID < tokens[j].AccessorID
   305  	})
   306  }
   307  
   308  type ACLPolicy struct {
   309  	// This is the internal UUID associated with the policy
   310  	ID string
   311  
   312  	// Unique name to reference the policy by.
   313  	//   - Valid Characters: [a-zA-Z0-9-]
   314  	//   - Valid Lengths: 1 - 128
   315  	Name string
   316  
   317  	// Human readable description (Optional)
   318  	Description string
   319  
   320  	// The rule set (using the updated rule syntax)
   321  	Rules string
   322  
   323  	// DEPRECATED (ACL-Legacy-Compat) - This is only needed while we support the legacy ACLs
   324  	Syntax acl.SyntaxVersion `json:"-"`
   325  
   326  	// Datacenters that the policy is valid within.
   327  	//   - No wildcards allowed
   328  	//   - If empty then the policy is valid within all datacenters
   329  	Datacenters []string `json:",omitempty"`
   330  
   331  	// Hash of the contents of the policy
   332  	// This does not take into account the ID (which is immutable)
   333  	// nor the raft metadata.
   334  	//
   335  	// This is needed mainly for replication purposes. When replicating from
   336  	// one DC to another keeping the content Hash will allow us to avoid
   337  	// unnecessary calls to the authoritative DC
   338  	Hash []byte
   339  
   340  	// Embedded Raft Metadata
   341  	RaftIndex `hash:"ignore"`
   342  }
   343  
   344  func (p *ACLPolicy) Clone() *ACLPolicy {
   345  	p2 := *p
   346  	p2.Datacenters = nil
   347  	if len(p.Datacenters) > 0 {
   348  		p2.Datacenters = make([]string, len(p.Datacenters))
   349  		copy(p2.Datacenters, p.Datacenters)
   350  	}
   351  	return &p2
   352  }
   353  
   354  type ACLPolicyListStub struct {
   355  	ID          string
   356  	Name        string
   357  	Description string
   358  	Datacenters []string
   359  	Hash        []byte
   360  	CreateIndex uint64
   361  	ModifyIndex uint64
   362  }
   363  
   364  func (p *ACLPolicy) Stub() *ACLPolicyListStub {
   365  	return &ACLPolicyListStub{
   366  		ID:          p.ID,
   367  		Name:        p.Name,
   368  		Description: p.Description,
   369  		Datacenters: p.Datacenters,
   370  		Hash:        p.Hash,
   371  		CreateIndex: p.CreateIndex,
   372  		ModifyIndex: p.ModifyIndex,
   373  	}
   374  }
   375  
   376  type ACLPolicies []*ACLPolicy
   377  type ACLPolicyListStubs []*ACLPolicyListStub
   378  
   379  func (p *ACLPolicy) SetHash(force bool) []byte {
   380  	if force || p.Hash == nil {
   381  		// Initialize a 256bit Blake2 hash (32 bytes)
   382  		hash, err := blake2b.New256(nil)
   383  		if err != nil {
   384  			panic(err)
   385  		}
   386  
   387  		// Write all the user set fields
   388  		hash.Write([]byte(p.Name))
   389  		hash.Write([]byte(p.Description))
   390  		hash.Write([]byte(p.Rules))
   391  		for _, dc := range p.Datacenters {
   392  			hash.Write([]byte(dc))
   393  		}
   394  
   395  		// Finalize the hash
   396  		hashVal := hash.Sum(nil)
   397  
   398  		// Set and return the hash
   399  		p.Hash = hashVal
   400  	}
   401  	return p.Hash
   402  }
   403  
   404  func (p *ACLPolicy) EstimateSize() int {
   405  	// This is just an estimate. There is other data structure overhead
   406  	// pointers etc that this does not account for.
   407  
   408  	// 64 = 36 (uuid) + 16 (RaftIndex) + 8 (Hash) + 4 (Syntax)
   409  	size := 64 + len(p.Name) + len(p.Description) + len(p.Rules)
   410  	for _, dc := range p.Datacenters {
   411  		size += len(dc)
   412  	}
   413  
   414  	return size
   415  }
   416  
   417  // ACLPolicyListHash returns a consistent hash for a set of policies.
   418  func (policies ACLPolicies) HashKey() string {
   419  	cacheKeyHash, err := blake2b.New256(nil)
   420  	if err != nil {
   421  		panic(err)
   422  	}
   423  	for _, policy := range policies {
   424  		cacheKeyHash.Write([]byte(policy.ID))
   425  		// including the modify index prevents a policy set from being
   426  		// cached if one of the policies has changed
   427  		binary.Write(cacheKeyHash, binary.BigEndian, policy.ModifyIndex)
   428  	}
   429  	return fmt.Sprintf("%x", cacheKeyHash.Sum(nil))
   430  }
   431  
   432  func (policies ACLPolicies) Sort() {
   433  	sort.Slice(policies, func(i, j int) bool {
   434  		return policies[i].ID < policies[j].ID
   435  	})
   436  }
   437  
   438  func (policies ACLPolicyListStubs) Sort() {
   439  	sort.Slice(policies, func(i, j int) bool {
   440  		return policies[i].ID < policies[j].ID
   441  	})
   442  }
   443  
   444  func (policies ACLPolicies) resolveWithCache(cache *ACLCaches, sentinel sentinel.Evaluator) ([]*acl.Policy, error) {
   445  	// Parse the policies
   446  	parsed := make([]*acl.Policy, 0, len(policies))
   447  	for _, policy := range policies {
   448  		policy.SetHash(false)
   449  		cacheKey := fmt.Sprintf("%x", policy.Hash)
   450  		cachedPolicy := cache.GetParsedPolicy(cacheKey)
   451  		if cachedPolicy != nil {
   452  			// policies are content hashed so no need to check the age
   453  			parsed = append(parsed, cachedPolicy.Policy)
   454  			continue
   455  		}
   456  
   457  		p, err := acl.NewPolicyFromSource(policy.ID, policy.ModifyIndex, policy.Rules, policy.Syntax, sentinel)
   458  		if err != nil {
   459  			return nil, fmt.Errorf("failed to parse %q: %v", policy.Name, err)
   460  		}
   461  
   462  		cache.PutParsedPolicy(cacheKey, p)
   463  		parsed = append(parsed, p)
   464  	}
   465  
   466  	return parsed, nil
   467  }
   468  
   469  func (policies ACLPolicies) Compile(parent acl.Authorizer, cache *ACLCaches, sentinel sentinel.Evaluator) (acl.Authorizer, error) {
   470  	// Determine the cache key
   471  	cacheKey := policies.HashKey()
   472  	entry := cache.GetAuthorizer(cacheKey)
   473  	if entry != nil {
   474  		// the hash key takes into account the policy contents. There is no reason to expire this cache or check its age.
   475  		return entry.Authorizer, nil
   476  	}
   477  
   478  	parsed, err := policies.resolveWithCache(cache, sentinel)
   479  	if err != nil {
   480  		return nil, fmt.Errorf("failed to parse the ACL policies: %v", err)
   481  	}
   482  
   483  	// Create the ACL object
   484  	authorizer, err := acl.NewPolicyAuthorizer(parent, parsed, sentinel)
   485  	if err != nil {
   486  		return nil, fmt.Errorf("failed to construct ACL Authorizer: %v", err)
   487  	}
   488  
   489  	// Update the cache
   490  	cache.PutAuthorizer(cacheKey, authorizer)
   491  	return authorizer, nil
   492  }
   493  
   494  func (policies ACLPolicies) Merge(cache *ACLCaches, sentinel sentinel.Evaluator) (*acl.Policy, error) {
   495  	parsed, err := policies.resolveWithCache(cache, sentinel)
   496  	if err != nil {
   497  		return nil, err
   498  	}
   499  
   500  	return acl.MergePolicies(parsed), nil
   501  }
   502  
   503  type ACLReplicationType string
   504  
   505  const (
   506  	ACLReplicateLegacy   ACLReplicationType = "legacy"
   507  	ACLReplicatePolicies ACLReplicationType = "policies"
   508  	ACLReplicateTokens   ACLReplicationType = "tokens"
   509  )
   510  
   511  // ACLReplicationStatus provides information about the health of the ACL
   512  // replication system.
   513  type ACLReplicationStatus struct {
   514  	Enabled              bool
   515  	Running              bool
   516  	SourceDatacenter     string
   517  	ReplicationType      ACLReplicationType
   518  	ReplicatedIndex      uint64
   519  	ReplicatedTokenIndex uint64
   520  	LastSuccess          time.Time
   521  	LastError            time.Time
   522  }
   523  
   524  // ACLTokenSetRequest is used for token creation and update operations
   525  // at the RPC layer
   526  type ACLTokenSetRequest struct {
   527  	ACLToken   ACLToken // Token to manipulate - I really dislike this name but "Token" is taken in the WriteRequest
   528  	Datacenter string   // The datacenter to perform the request within
   529  	WriteRequest
   530  }
   531  
   532  func (r *ACLTokenSetRequest) RequestDatacenter() string {
   533  	return r.Datacenter
   534  }
   535  
   536  // ACLTokenGetRequest is used for token read operations at the RPC layer
   537  type ACLTokenGetRequest struct {
   538  	TokenID     string         // id used for the token lookup
   539  	TokenIDType ACLTokenIDType // The Type of ID used to lookup the token
   540  	Datacenter  string         // The datacenter to perform the request within
   541  	QueryOptions
   542  }
   543  
   544  func (r *ACLTokenGetRequest) RequestDatacenter() string {
   545  	return r.Datacenter
   546  }
   547  
   548  // ACLTokenDeleteRequest is used for token deletion operations at the RPC layer
   549  type ACLTokenDeleteRequest struct {
   550  	TokenID    string // ID of the token to delete
   551  	Datacenter string // The datacenter to perform the request within
   552  	WriteRequest
   553  }
   554  
   555  func (r *ACLTokenDeleteRequest) RequestDatacenter() string {
   556  	return r.Datacenter
   557  }
   558  
   559  // ACLTokenListRequest is used for token listing operations at the RPC layer
   560  type ACLTokenListRequest struct {
   561  	IncludeLocal  bool   // Whether local tokens should be included
   562  	IncludeGlobal bool   // Whether global tokens should be included
   563  	Policy        string // Policy filter
   564  	Datacenter    string // The datacenter to perform the request within
   565  	QueryOptions
   566  }
   567  
   568  func (r *ACLTokenListRequest) RequestDatacenter() string {
   569  	return r.Datacenter
   570  }
   571  
   572  // ACLTokenListResponse is used to return the secret data free stubs
   573  // of the tokens
   574  type ACLTokenListResponse struct {
   575  	Tokens ACLTokenListStubs
   576  	QueryMeta
   577  }
   578  
   579  // ACLTokenBatchGetRequest is used for reading multiple tokens, this is
   580  // different from the the token list request in that only tokens with the
   581  // the requested ids are returned
   582  type ACLTokenBatchGetRequest struct {
   583  	AccessorIDs []string // List of accessor ids to fetch
   584  	Datacenter  string   // The datacenter to perform the request within
   585  	QueryOptions
   586  }
   587  
   588  func (r *ACLTokenBatchGetRequest) RequestDatacenter() string {
   589  	return r.Datacenter
   590  }
   591  
   592  // ACLTokenBatchSetRequest is used only at the Raft layer
   593  // for batching multiple token creation/update operations
   594  //
   595  // This is particularly useful during token replication and during
   596  // automatic legacy token upgrades.
   597  type ACLTokenBatchSetRequest struct {
   598  	Tokens ACLTokens
   599  	CAS    bool
   600  }
   601  
   602  // ACLTokenBatchDeleteRequest is used only at the Raft layer
   603  // for batching multiple token deletions.
   604  //
   605  // This is particularly useful during token replication when
   606  // multiple tokens need to be removed from the local DCs state.
   607  type ACLTokenBatchDeleteRequest struct {
   608  	TokenIDs []string // Tokens to delete
   609  }
   610  
   611  // ACLTokenBootstrapRequest is used only at the Raft layer
   612  // for ACL bootstrapping
   613  //
   614  // The RPC layer will use a generic DCSpecificRequest to indicate
   615  // that bootstrapping must be performed but the actual token
   616  // and the resetIndex will be generated by that RPC endpoint
   617  type ACLTokenBootstrapRequest struct {
   618  	Token      ACLToken // Token to use for bootstrapping
   619  	ResetIndex uint64   // Reset index
   620  }
   621  
   622  // ACLTokenResponse returns a single Token + metadata
   623  type ACLTokenResponse struct {
   624  	Token    *ACLToken
   625  	Redacted bool // whether the token's secret was redacted
   626  	QueryMeta
   627  }
   628  
   629  // ACLTokenBatchResponse returns multiple Tokens associated with the same metadata
   630  type ACLTokenBatchResponse struct {
   631  	Tokens   []*ACLToken
   632  	Redacted bool // whether the token secrets were redacted.
   633  	QueryMeta
   634  }
   635  
   636  // ACLPolicySetRequest is used at the RPC layer for creation and update requests
   637  type ACLPolicySetRequest struct {
   638  	Policy     ACLPolicy // The policy to upsert
   639  	Datacenter string    // The datacenter to perform the request within
   640  	WriteRequest
   641  }
   642  
   643  func (r *ACLPolicySetRequest) RequestDatacenter() string {
   644  	return r.Datacenter
   645  }
   646  
   647  // ACLPolicyDeleteRequest is used at the RPC layer deletion requests
   648  type ACLPolicyDeleteRequest struct {
   649  	PolicyID   string // The id of the policy to delete
   650  	Datacenter string // The datacenter to perform the request within
   651  	WriteRequest
   652  }
   653  
   654  func (r *ACLPolicyDeleteRequest) RequestDatacenter() string {
   655  	return r.Datacenter
   656  }
   657  
   658  // ACLPolicyGetRequest is used at the RPC layer to perform policy read operations
   659  type ACLPolicyGetRequest struct {
   660  	PolicyID   string // id used for the policy lookup
   661  	Datacenter string // The datacenter to perform the request within
   662  	QueryOptions
   663  }
   664  
   665  func (r *ACLPolicyGetRequest) RequestDatacenter() string {
   666  	return r.Datacenter
   667  }
   668  
   669  // ACLPolicyListRequest is used at the RPC layer to request a listing of policies
   670  type ACLPolicyListRequest struct {
   671  	Datacenter string // The datacenter to perform the request within
   672  	QueryOptions
   673  }
   674  
   675  func (r *ACLPolicyListRequest) RequestDatacenter() string {
   676  	return r.Datacenter
   677  }
   678  
   679  type ACLPolicyListResponse struct {
   680  	Policies ACLPolicyListStubs
   681  	QueryMeta
   682  }
   683  
   684  // ACLPolicyBatchGetRequest is used at the RPC layer to request a subset of
   685  // the policies associated with the token used for retrieval
   686  type ACLPolicyBatchGetRequest struct {
   687  	PolicyIDs  []string // List of policy ids to fetch
   688  	Datacenter string   // The datacenter to perform the request within
   689  	QueryOptions
   690  }
   691  
   692  func (r *ACLPolicyBatchGetRequest) RequestDatacenter() string {
   693  	return r.Datacenter
   694  }
   695  
   696  // ACLPolicyResponse returns a single policy + metadata
   697  type ACLPolicyResponse struct {
   698  	Policy *ACLPolicy
   699  	QueryMeta
   700  }
   701  
   702  type ACLPolicyBatchResponse struct {
   703  	Policies []*ACLPolicy
   704  	QueryMeta
   705  }
   706  
   707  // ACLPolicyBatchSetRequest is used at the Raft layer for batching
   708  // multiple policy creations and updates
   709  //
   710  // This is particularly useful during replication
   711  type ACLPolicyBatchSetRequest struct {
   712  	Policies ACLPolicies
   713  }
   714  
   715  // ACLPolicyBatchDeleteRequest is used at the Raft layer for batching
   716  // multiple policy deletions
   717  //
   718  // This is particularly useful during replication
   719  type ACLPolicyBatchDeleteRequest struct {
   720  	PolicyIDs []string
   721  }