github.com/clly/consul@v1.4.5/agent/consul/state/acl.go (about)

     1  package state
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/consul/agent/structs"
     7  	"github.com/hashicorp/go-memdb"
     8  )
     9  
    10  type TokenPoliciesIndex struct {
    11  }
    12  
    13  func (s *TokenPoliciesIndex) FromObject(obj interface{}) (bool, [][]byte, error) {
    14  	token, ok := obj.(*structs.ACLToken)
    15  	if !ok {
    16  		return false, nil, fmt.Errorf("object is not an ACLToken")
    17  	}
    18  
    19  	links := token.Policies
    20  
    21  	numLinks := len(links)
    22  	if numLinks == 0 {
    23  		return false, nil, nil
    24  	}
    25  
    26  	vals := make([][]byte, 0, numLinks)
    27  	for _, link := range links {
    28  		vals = append(vals, []byte(link.ID+"\x00"))
    29  	}
    30  
    31  	return true, vals, nil
    32  }
    33  
    34  func (s *TokenPoliciesIndex) FromArgs(args ...interface{}) ([]byte, error) {
    35  	if len(args) != 1 {
    36  		return nil, fmt.Errorf("must provide only a single argument")
    37  	}
    38  	arg, ok := args[0].(string)
    39  	if !ok {
    40  		return nil, fmt.Errorf("argument must be a string: %#v", args[0])
    41  	}
    42  	// Add the null character as a terminator
    43  	arg += "\x00"
    44  	return []byte(arg), nil
    45  }
    46  
    47  func (s *TokenPoliciesIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
    48  	val, err := s.FromArgs(args...)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  
    53  	// Strip the null terminator, the rest is a prefix
    54  	n := len(val)
    55  	if n > 0 {
    56  		return val[:n-1], nil
    57  	}
    58  	return val, nil
    59  }
    60  
    61  func tokensTableSchema() *memdb.TableSchema {
    62  	return &memdb.TableSchema{
    63  		Name: "acl-tokens",
    64  		Indexes: map[string]*memdb.IndexSchema{
    65  			"accessor": &memdb.IndexSchema{
    66  				Name: "accessor",
    67  				// DEPRECATED (ACL-Legacy-Compat) - we should not AllowMissing here once legacy compat is removed
    68  				AllowMissing: true,
    69  				Unique:       true,
    70  				Indexer: &memdb.UUIDFieldIndex{
    71  					Field: "AccessorID",
    72  				},
    73  			},
    74  			"id": &memdb.IndexSchema{
    75  				Name:         "id",
    76  				AllowMissing: false,
    77  				Unique:       true,
    78  				Indexer: &memdb.StringFieldIndex{
    79  					Field:     "SecretID",
    80  					Lowercase: false,
    81  				},
    82  			},
    83  			"policies": &memdb.IndexSchema{
    84  				Name: "policies",
    85  				// Need to allow missing for the anonymous token
    86  				AllowMissing: true,
    87  				Unique:       false,
    88  				Indexer:      &TokenPoliciesIndex{},
    89  			},
    90  			"local": &memdb.IndexSchema{
    91  				Name:         "local",
    92  				AllowMissing: false,
    93  				Unique:       false,
    94  				Indexer: &memdb.ConditionalIndex{
    95  					Conditional: func(obj interface{}) (bool, error) {
    96  						if token, ok := obj.(*structs.ACLToken); ok {
    97  							return token.Local, nil
    98  						}
    99  						return false, nil
   100  					},
   101  				},
   102  			},
   103  
   104  			//DEPRECATED (ACL-Legacy-Compat) - This index is only needed while we support upgrading v1 to v2 acls
   105  			// This table indexes all the ACL tokens that do not have an AccessorID
   106  			"needs-upgrade": &memdb.IndexSchema{
   107  				Name:         "needs-upgrade",
   108  				AllowMissing: false,
   109  				Unique:       false,
   110  				Indexer: &memdb.ConditionalIndex{
   111  					Conditional: func(obj interface{}) (bool, error) {
   112  						if token, ok := obj.(*structs.ACLToken); ok {
   113  							return token.AccessorID == "", nil
   114  						}
   115  						return false, nil
   116  					},
   117  				},
   118  			},
   119  		},
   120  	}
   121  }
   122  
   123  func policiesTableSchema() *memdb.TableSchema {
   124  	return &memdb.TableSchema{
   125  		Name: "acl-policies",
   126  		Indexes: map[string]*memdb.IndexSchema{
   127  			"id": &memdb.IndexSchema{
   128  				Name:         "id",
   129  				AllowMissing: false,
   130  				Unique:       true,
   131  				Indexer: &memdb.UUIDFieldIndex{
   132  					Field: "ID",
   133  				},
   134  			},
   135  			"name": &memdb.IndexSchema{
   136  				Name:         "name",
   137  				AllowMissing: false,
   138  				Unique:       true,
   139  				Indexer: &memdb.StringFieldIndex{
   140  					Field: "Name",
   141  					// TODO (ACL-V2) - should we coerce to lowercase?
   142  					Lowercase: true,
   143  				},
   144  			},
   145  		},
   146  	}
   147  }
   148  
   149  func init() {
   150  	registerSchema(tokensTableSchema)
   151  	registerSchema(policiesTableSchema)
   152  }
   153  
   154  // ACLTokens is used when saving a snapshot
   155  func (s *Snapshot) ACLTokens() (memdb.ResultIterator, error) {
   156  	// DEPRECATED (ACL-Legacy-Compat) - This could use the "id" index when we remove v1 compat
   157  	iter, err := s.tx.Get("acl-tokens", "id")
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	return iter, nil
   162  }
   163  
   164  // ACLToken is used when restoring from a snapshot. For general inserts, use ACL.
   165  func (s *Restore) ACLToken(token *structs.ACLToken) error {
   166  	if err := s.tx.Insert("acl-tokens", token); err != nil {
   167  		return fmt.Errorf("failed restoring acl token: %s", err)
   168  	}
   169  
   170  	if err := indexUpdateMaxTxn(s.tx, token.ModifyIndex, "acl-tokens"); err != nil {
   171  		return fmt.Errorf("failed updating index: %s", err)
   172  	}
   173  	return nil
   174  }
   175  
   176  // ACLPolicies is used when saving a snapshot
   177  func (s *Snapshot) ACLPolicies() (memdb.ResultIterator, error) {
   178  	iter, err := s.tx.Get("acl-policies", "id")
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	return iter, nil
   183  }
   184  
   185  func (s *Restore) ACLPolicy(policy *structs.ACLPolicy) error {
   186  	if err := s.tx.Insert("acl-policies", policy); err != nil {
   187  		return fmt.Errorf("failed restoring acl policy: %s", err)
   188  	}
   189  
   190  	if err := indexUpdateMaxTxn(s.tx, policy.ModifyIndex, "acl-policies"); err != nil {
   191  		return fmt.Errorf("failed updating index: %s", err)
   192  	}
   193  	return nil
   194  }
   195  
   196  // ACLBootstrap is used to perform a one-time ACL bootstrap operation on a
   197  // cluster to get the first management token.
   198  func (s *Store) ACLBootstrap(idx, resetIndex uint64, token *structs.ACLToken, legacy bool) error {
   199  	tx := s.db.Txn(true)
   200  	defer tx.Abort()
   201  
   202  	// We must have initialized before this will ever be possible.
   203  	existing, err := tx.First("index", "id", "acl-token-bootstrap")
   204  	if err != nil {
   205  		return fmt.Errorf("bootstrap check failed: %v", err)
   206  	}
   207  	if existing != nil {
   208  		if resetIndex == 0 {
   209  			return structs.ACLBootstrapNotAllowedErr
   210  		} else if resetIndex != existing.(*IndexEntry).Value {
   211  			return structs.ACLBootstrapInvalidResetIndexErr
   212  		}
   213  	}
   214  
   215  	if err := s.aclTokenSetTxn(tx, idx, token, false, false, legacy); err != nil {
   216  		return fmt.Errorf("failed inserting bootstrap token: %v", err)
   217  	}
   218  	if err := indexUpdateMaxTxn(tx, idx, "acl-tokens"); err != nil {
   219  		return fmt.Errorf("failed updating index: %s", err)
   220  	}
   221  	if err := tx.Insert("index", &IndexEntry{"acl-token-bootstrap", idx}); err != nil {
   222  		return fmt.Errorf("failed to mark ACL bootstrapping as complete: %v", err)
   223  	}
   224  	tx.Commit()
   225  	return nil
   226  }
   227  
   228  // CanBootstrapACLToken checks if bootstrapping is possible and returns the reset index
   229  func (s *Store) CanBootstrapACLToken() (bool, uint64, error) {
   230  	txn := s.db.Txn(false)
   231  
   232  	// Lookup the bootstrap sentinel
   233  	out, err := txn.First("index", "id", "acl-token-bootstrap")
   234  	if err != nil {
   235  		return false, 0, err
   236  	}
   237  
   238  	// No entry, we haven't bootstrapped yet
   239  	if out == nil {
   240  		return true, 0, nil
   241  	}
   242  
   243  	// Return the reset index if we've already bootstrapped
   244  	return false, out.(*IndexEntry).Value, nil
   245  }
   246  
   247  func (s *Store) resolveTokenPolicyLinks(tx *memdb.Txn, token *structs.ACLToken, allowMissing bool) error {
   248  	for linkIndex, link := range token.Policies {
   249  		if link.ID != "" {
   250  			policy, err := s.getPolicyWithTxn(tx, nil, link.ID, "id")
   251  
   252  			if err != nil {
   253  				return err
   254  			}
   255  
   256  			if policy != nil {
   257  				// the name doesn't matter here
   258  				token.Policies[linkIndex].Name = policy.Name
   259  			} else if !allowMissing {
   260  				return fmt.Errorf("No such policy with ID: %s", link.ID)
   261  			}
   262  		} else {
   263  			return fmt.Errorf("Encountered a Token with policies linked by Name in the state store")
   264  		}
   265  	}
   266  	return nil
   267  }
   268  
   269  // fixupTokenPolicyLinks is to be used when retrieving tokens from memdb. The policy links could have gotten
   270  // stale when a linked policy was deleted or renamed. This will correct them and generate a newly allocated
   271  // token only when fixes are needed. If the policy links are still accurate then we just return the original
   272  // token.
   273  func (s *Store) fixupTokenPolicyLinks(tx *memdb.Txn, original *structs.ACLToken) (*structs.ACLToken, error) {
   274  	owned := false
   275  	token := original
   276  
   277  	cloneToken := func(t *structs.ACLToken, copyNumLinks int) *structs.ACLToken {
   278  		clone := *t
   279  		clone.Policies = make([]structs.ACLTokenPolicyLink, copyNumLinks)
   280  		copy(clone.Policies, t.Policies[:copyNumLinks])
   281  		return &clone
   282  	}
   283  
   284  	for linkIndex, link := range original.Policies {
   285  		if link.ID == "" {
   286  			return nil, fmt.Errorf("Detected corrupted token within the state store - missing policy link ID")
   287  		}
   288  
   289  		policy, err := s.getPolicyWithTxn(tx, nil, link.ID, "id")
   290  
   291  		if err != nil {
   292  			return nil, err
   293  		}
   294  
   295  		if policy == nil {
   296  			if !owned {
   297  				// clone the token as we cannot touch the original
   298  				token = cloneToken(original, linkIndex)
   299  				owned = true
   300  			}
   301  			// if already owned then we just don't append it.
   302  		} else if policy.Name != link.Name {
   303  			if !owned {
   304  				token = cloneToken(original, linkIndex)
   305  				owned = true
   306  			}
   307  
   308  			// append the corrected policy
   309  			token.Policies = append(token.Policies, structs.ACLTokenPolicyLink{ID: link.ID, Name: policy.Name})
   310  		} else if owned {
   311  			token.Policies = append(token.Policies, link)
   312  		}
   313  	}
   314  
   315  	return token, nil
   316  }
   317  
   318  // ACLTokenSet is used to insert an ACL rule into the state store.
   319  func (s *Store) ACLTokenSet(idx uint64, token *structs.ACLToken, legacy bool) error {
   320  	tx := s.db.Txn(true)
   321  	defer tx.Abort()
   322  
   323  	// Call set on the ACL
   324  	if err := s.aclTokenSetTxn(tx, idx, token, false, false, legacy); err != nil {
   325  		return err
   326  	}
   327  
   328  	if err := indexUpdateMaxTxn(tx, idx, "acl-tokens"); err != nil {
   329  		return fmt.Errorf("failed updating index: %s", err)
   330  	}
   331  
   332  	tx.Commit()
   333  	return nil
   334  }
   335  
   336  func (s *Store) ACLTokenBatchSet(idx uint64, tokens structs.ACLTokens, cas bool) error {
   337  	tx := s.db.Txn(true)
   338  	defer tx.Abort()
   339  
   340  	for _, token := range tokens {
   341  		// this is only used when doing batch insertions for upgrades and replication. Therefore
   342  		// we take whatever those said.
   343  		if err := s.aclTokenSetTxn(tx, idx, token, cas, true, false); err != nil {
   344  			return err
   345  		}
   346  	}
   347  
   348  	if err := indexUpdateMaxTxn(tx, idx, "acl-tokens"); err != nil {
   349  		return fmt.Errorf("failed updating index: %s", err)
   350  	}
   351  
   352  	tx.Commit()
   353  	return nil
   354  }
   355  
   356  // aclTokenSetTxn is the inner method used to insert an ACL token with the
   357  // proper indexes into the state store.
   358  func (s *Store) aclTokenSetTxn(tx *memdb.Txn, idx uint64, token *structs.ACLToken, cas, allowMissingPolicyIDs, legacy bool) error {
   359  	// Check that the ID is set
   360  	if token.SecretID == "" {
   361  		return ErrMissingACLTokenSecret
   362  	}
   363  
   364  	if !legacy && token.AccessorID == "" {
   365  		return ErrMissingACLTokenAccessor
   366  	}
   367  
   368  	// DEPRECATED (ACL-Legacy-Compat)
   369  	if token.Rules != "" {
   370  		// When we update a legacy acl token we may have to correct old HCL to
   371  		// prevent the propagation of older syntax into the state store and
   372  		// into in-memory representations.
   373  		correctedRules := structs.SanitizeLegacyACLTokenRules(token.Rules)
   374  		if correctedRules != "" {
   375  			token.Rules = correctedRules
   376  		}
   377  	}
   378  
   379  	// Check for an existing ACL
   380  	// DEPRECATED (ACL-Legacy-Compat) - transition to using accessor index instead of secret once v1 compat is removed
   381  	existing, err := tx.First("acl-tokens", "id", token.SecretID)
   382  	if err != nil {
   383  		return fmt.Errorf("failed token lookup: %s", err)
   384  	}
   385  
   386  	var original *structs.ACLToken
   387  
   388  	if existing != nil {
   389  		original = existing.(*structs.ACLToken)
   390  	}
   391  
   392  	if cas {
   393  		// set-if-unset case
   394  		if token.ModifyIndex == 0 && original != nil {
   395  			return nil
   396  		}
   397  		// token already deleted
   398  		if token.ModifyIndex != 0 && original == nil {
   399  			return nil
   400  		}
   401  		// check for other modifications
   402  		if token.ModifyIndex != 0 && token.ModifyIndex != original.ModifyIndex {
   403  			return nil
   404  		}
   405  	}
   406  
   407  	if legacy && original != nil {
   408  		if len(original.Policies) > 0 || original.Type == "" {
   409  			return fmt.Errorf("failed inserting acl token: cannot use legacy endpoint to modify a non-legacy token")
   410  		}
   411  
   412  		token.AccessorID = original.AccessorID
   413  	}
   414  
   415  	if err := s.resolveTokenPolicyLinks(tx, token, allowMissingPolicyIDs); err != nil {
   416  		return err
   417  	}
   418  
   419  	// Set the indexes
   420  	if original != nil {
   421  		if original.AccessorID != "" && token.AccessorID != original.AccessorID {
   422  			return fmt.Errorf("The ACL Token AccessorID field is immutable")
   423  		}
   424  
   425  		if token.SecretID != original.SecretID {
   426  			return fmt.Errorf("The ACL Token SecretID field is immutable")
   427  		}
   428  
   429  		token.CreateIndex = original.CreateIndex
   430  		token.ModifyIndex = idx
   431  	} else {
   432  		token.CreateIndex = idx
   433  		token.ModifyIndex = idx
   434  	}
   435  
   436  	// Insert the ACL
   437  	if err := tx.Insert("acl-tokens", token); err != nil {
   438  		return fmt.Errorf("failed inserting acl token: %v", err)
   439  	}
   440  
   441  	return nil
   442  }
   443  
   444  // ACLTokenGetBySecret is used to look up an existing ACL token by its SecretID.
   445  func (s *Store) ACLTokenGetBySecret(ws memdb.WatchSet, secret string) (uint64, *structs.ACLToken, error) {
   446  	return s.aclTokenGet(ws, secret, "id")
   447  }
   448  
   449  // ACLTokenGetByAccessor is used to look up an existing ACL token by its AccessorID.
   450  func (s *Store) ACLTokenGetByAccessor(ws memdb.WatchSet, accessor string) (uint64, *structs.ACLToken, error) {
   451  	return s.aclTokenGet(ws, accessor, "accessor")
   452  }
   453  
   454  // aclTokenGet looks up a token using one of the indexes provided
   455  func (s *Store) aclTokenGet(ws memdb.WatchSet, value, index string) (uint64, *structs.ACLToken, error) {
   456  	tx := s.db.Txn(false)
   457  	defer tx.Abort()
   458  
   459  	token, err := s.aclTokenGetTxn(tx, ws, value, index)
   460  	if err != nil {
   461  		return 0, nil, fmt.Errorf("failed acl token lookup: %v", err)
   462  	}
   463  
   464  	idx := maxIndexTxn(tx, "acl-tokens")
   465  	return idx, token, nil
   466  }
   467  
   468  func (s *Store) ACLTokenBatchGet(ws memdb.WatchSet, accessors []string) (uint64, structs.ACLTokens, error) {
   469  	tx := s.db.Txn(false)
   470  	defer tx.Abort()
   471  
   472  	tokens := make(structs.ACLTokens, 0)
   473  	for _, accessor := range accessors {
   474  		token, err := s.aclTokenGetTxn(tx, ws, accessor, "accessor")
   475  		if err != nil {
   476  			return 0, nil, fmt.Errorf("failed acl token lookup: %v", err)
   477  		}
   478  
   479  		// token == nil is valid and will indic
   480  		if token != nil {
   481  			tokens = append(tokens, token)
   482  		}
   483  	}
   484  
   485  	idx := maxIndexTxn(tx, "acl-tokens")
   486  
   487  	return idx, tokens, nil
   488  }
   489  
   490  func (s *Store) aclTokenGetTxn(tx *memdb.Txn, ws memdb.WatchSet, value, index string) (*structs.ACLToken, error) {
   491  	watchCh, rawToken, err := tx.FirstWatch("acl-tokens", index, value)
   492  	if err != nil {
   493  		return nil, fmt.Errorf("failed acl token lookup: %v", err)
   494  	}
   495  	ws.Add(watchCh)
   496  
   497  	if rawToken != nil {
   498  		token, err := s.fixupTokenPolicyLinks(tx, rawToken.(*structs.ACLToken))
   499  		if err != nil {
   500  			return nil, err
   501  		}
   502  		return token, nil
   503  	}
   504  
   505  	return nil, nil
   506  }
   507  
   508  // ACLTokenList is used to list out all of the ACLs in the state store.
   509  func (s *Store) ACLTokenList(ws memdb.WatchSet, local, global bool, policy string) (uint64, structs.ACLTokens, error) {
   510  	tx := s.db.Txn(false)
   511  	defer tx.Abort()
   512  
   513  	var iter memdb.ResultIterator
   514  	var err error
   515  
   516  	// Note global == local works when both are true or false. It is not valid to set both
   517  	// to false but for defaulted structs (zero values for both) we want it to list out
   518  	// all tokens so our checks just ensure that global == local
   519  
   520  	if policy != "" {
   521  		iter, err = tx.Get("acl-tokens", "policies", policy)
   522  		if err == nil && global != local {
   523  			iter = memdb.NewFilterIterator(iter, func(raw interface{}) bool {
   524  				token, ok := raw.(*structs.ACLToken)
   525  				if !ok {
   526  					return false
   527  				}
   528  
   529  				if global && !token.Local {
   530  					return true
   531  				} else if local && token.Local {
   532  					return true
   533  				}
   534  
   535  				return false
   536  			})
   537  		}
   538  	} else if global == local {
   539  		iter, err = tx.Get("acl-tokens", "id")
   540  	} else if global {
   541  		iter, err = tx.Get("acl-tokens", "local", false)
   542  	} else {
   543  		iter, err = tx.Get("acl-tokens", "local", true)
   544  	}
   545  
   546  	if err != nil {
   547  		return 0, nil, fmt.Errorf("failed acl token lookup: %v", err)
   548  	}
   549  	ws.Add(iter.WatchCh())
   550  
   551  	var result structs.ACLTokens
   552  	for raw := iter.Next(); raw != nil; raw = iter.Next() {
   553  		token, err := s.fixupTokenPolicyLinks(tx, raw.(*structs.ACLToken))
   554  
   555  		if err != nil {
   556  			return 0, nil, err
   557  		}
   558  		result = append(result, token)
   559  	}
   560  
   561  	// Get the table index.
   562  	idx := maxIndexTxn(tx, "acl-tokens")
   563  
   564  	return idx, result, nil
   565  }
   566  
   567  func (s *Store) ACLTokenListUpgradeable(max int) (structs.ACLTokens, <-chan struct{}, error) {
   568  	tx := s.db.Txn(false)
   569  	defer tx.Abort()
   570  
   571  	iter, err := tx.Get("acl-tokens", "needs-upgrade", true)
   572  	if err != nil {
   573  		return nil, nil, fmt.Errorf("failed acl token listing: %v", err)
   574  	}
   575  
   576  	var tokens structs.ACLTokens
   577  	i := 0
   578  	for token := iter.Next(); token != nil; token = iter.Next() {
   579  		tokens = append(tokens, token.(*structs.ACLToken))
   580  		i += 1
   581  		if i >= max {
   582  			return tokens, nil, nil
   583  		}
   584  	}
   585  
   586  	return tokens, iter.WatchCh(), nil
   587  }
   588  
   589  // ACLTokenDeleteBySecret is used to remove an existing ACL from the state store. If
   590  // the ACL does not exist this is a no-op and no error is returned.
   591  func (s *Store) ACLTokenDeleteBySecret(idx uint64, secret string) error {
   592  	return s.aclTokenDelete(idx, secret, "id")
   593  }
   594  
   595  // ACLTokenDeleteByAccessor is used to remove an existing ACL from the state store. If
   596  // the ACL does not exist this is a no-op and no error is returned.
   597  func (s *Store) ACLTokenDeleteByAccessor(idx uint64, accessor string) error {
   598  	return s.aclTokenDelete(idx, accessor, "accessor")
   599  }
   600  
   601  func (s *Store) ACLTokenBatchDelete(idx uint64, tokenIDs []string) error {
   602  	tx := s.db.Txn(true)
   603  	defer tx.Abort()
   604  
   605  	for _, tokenID := range tokenIDs {
   606  		if err := s.aclTokenDeleteTxn(tx, idx, tokenID, "accessor"); err != nil {
   607  			return err
   608  		}
   609  	}
   610  
   611  	tx.Commit()
   612  	return nil
   613  }
   614  
   615  func (s *Store) aclTokenDelete(idx uint64, value, index string) error {
   616  	tx := s.db.Txn(true)
   617  	defer tx.Abort()
   618  
   619  	if err := s.aclTokenDeleteTxn(tx, idx, value, index); err != nil {
   620  		return err
   621  	}
   622  
   623  	tx.Commit()
   624  	return nil
   625  }
   626  
   627  func (s *Store) aclTokenDeleteTxn(tx *memdb.Txn, idx uint64, value, index string) error {
   628  	// Look up the existing token
   629  	token, err := tx.First("acl-tokens", index, value)
   630  	if err != nil {
   631  		return fmt.Errorf("failed acl token lookup: %v", err)
   632  	}
   633  
   634  	if token == nil {
   635  		return nil
   636  	}
   637  
   638  	if token.(*structs.ACLToken).AccessorID == structs.ACLTokenAnonymousID {
   639  		return fmt.Errorf("Deletion of the builtin anonymous token is not permitted")
   640  	}
   641  
   642  	if err := tx.Delete("acl-tokens", token); err != nil {
   643  		return fmt.Errorf("failed deleting acl token: %v", err)
   644  	}
   645  	if err := indexUpdateMaxTxn(tx, idx, "acl-tokens"); err != nil {
   646  		return fmt.Errorf("failed updating index: %v", err)
   647  	}
   648  	return nil
   649  }
   650  
   651  func (s *Store) ACLPolicyBatchSet(idx uint64, policies structs.ACLPolicies) error {
   652  	tx := s.db.Txn(true)
   653  	defer tx.Abort()
   654  
   655  	for _, policy := range policies {
   656  		if err := s.aclPolicySetTxn(tx, idx, policy); err != nil {
   657  			return err
   658  		}
   659  	}
   660  
   661  	if err := indexUpdateMaxTxn(tx, idx, "acl-policies"); err != nil {
   662  		return fmt.Errorf("failed updating index: %s", err)
   663  	}
   664  
   665  	tx.Commit()
   666  	return nil
   667  }
   668  
   669  func (s *Store) ACLPolicySet(idx uint64, policy *structs.ACLPolicy) error {
   670  	tx := s.db.Txn(true)
   671  	defer tx.Abort()
   672  
   673  	if err := s.aclPolicySetTxn(tx, idx, policy); err != nil {
   674  		return err
   675  	}
   676  	if err := indexUpdateMaxTxn(tx, idx, "acl-policies"); err != nil {
   677  		return fmt.Errorf("failed updating index: %s", err)
   678  	}
   679  
   680  	tx.Commit()
   681  	return nil
   682  }
   683  
   684  func (s *Store) aclPolicySetTxn(tx *memdb.Txn, idx uint64, policy *structs.ACLPolicy) error {
   685  	// Check that the ID is set
   686  	if policy.ID == "" {
   687  		return ErrMissingACLPolicyID
   688  	}
   689  
   690  	if policy.Name == "" {
   691  		return ErrMissingACLPolicyName
   692  	}
   693  
   694  	existing, err := tx.First("acl-policies", "id", policy.ID)
   695  	if err != nil {
   696  		return fmt.Errorf("failed acl policy lookup: %v", err)
   697  	}
   698  
   699  	if existing != nil {
   700  		policyMatch := existing.(*structs.ACLPolicy)
   701  
   702  		if policy.ID == structs.ACLPolicyGlobalManagementID {
   703  			// Only the name and description are modifiable
   704  			if policy.Rules != policyMatch.Rules {
   705  				return fmt.Errorf("Changing the Rules for the builtin global-management policy is not permitted")
   706  			}
   707  
   708  			if policy.Datacenters != nil && len(policy.Datacenters) != 0 {
   709  				return fmt.Errorf("Changing the Datacenters of the builtin global-management policy is not permitted")
   710  			}
   711  		}
   712  	}
   713  
   714  	// ensure the name is unique (cannot conflict with another policy with a different ID)
   715  	nameMatch, err := tx.First("acl-policies", "name", policy.Name)
   716  	if err != nil {
   717  		return fmt.Errorf("failed acl policy lookup: %v", err)
   718  	}
   719  	if nameMatch != nil && policy.ID != nameMatch.(*structs.ACLPolicy).ID {
   720  		return fmt.Errorf("A policy with name %q already exists", policy.Name)
   721  	}
   722  
   723  	// Set the indexes
   724  	if existing != nil {
   725  		policy.CreateIndex = existing.(*structs.ACLPolicy).CreateIndex
   726  		policy.ModifyIndex = idx
   727  	} else {
   728  		policy.CreateIndex = idx
   729  		policy.ModifyIndex = idx
   730  	}
   731  
   732  	// Insert the ACL
   733  	if err := tx.Insert("acl-policies", policy); err != nil {
   734  		return fmt.Errorf("failed inserting acl policy: %v", err)
   735  	}
   736  	return nil
   737  }
   738  
   739  func (s *Store) ACLPolicyGetByID(ws memdb.WatchSet, id string) (uint64, *structs.ACLPolicy, error) {
   740  	return s.aclPolicyGet(ws, id, "id")
   741  }
   742  
   743  func (s *Store) ACLPolicyGetByName(ws memdb.WatchSet, name string) (uint64, *structs.ACLPolicy, error) {
   744  	return s.aclPolicyGet(ws, name, "name")
   745  }
   746  
   747  func (s *Store) ACLPolicyBatchGet(ws memdb.WatchSet, ids []string) (uint64, structs.ACLPolicies, error) {
   748  	tx := s.db.Txn(false)
   749  	defer tx.Abort()
   750  
   751  	policies := make(structs.ACLPolicies, 0)
   752  	for _, pid := range ids {
   753  		policy, err := s.getPolicyWithTxn(tx, ws, pid, "id")
   754  		if err != nil {
   755  			return 0, nil, err
   756  		}
   757  
   758  		if policy != nil {
   759  			policies = append(policies, policy)
   760  		}
   761  	}
   762  
   763  	idx := maxIndexTxn(tx, "acl-policies")
   764  
   765  	return idx, policies, nil
   766  }
   767  
   768  func (s *Store) getPolicyWithTxn(tx *memdb.Txn, ws memdb.WatchSet, value, index string) (*structs.ACLPolicy, error) {
   769  	watchCh, policy, err := tx.FirstWatch("acl-policies", index, value)
   770  	if err != nil {
   771  		return nil, fmt.Errorf("failed acl policy lookup: %v", err)
   772  	}
   773  	ws.Add(watchCh)
   774  
   775  	if err != nil || policy == nil {
   776  		return nil, err
   777  	}
   778  
   779  	return policy.(*structs.ACLPolicy), nil
   780  }
   781  
   782  func (s *Store) aclPolicyGet(ws memdb.WatchSet, value, index string) (uint64, *structs.ACLPolicy, error) {
   783  	tx := s.db.Txn(false)
   784  	defer tx.Abort()
   785  
   786  	policy, err := s.getPolicyWithTxn(tx, ws, value, index)
   787  	if err != nil {
   788  		return 0, nil, err
   789  	}
   790  
   791  	idx := maxIndexTxn(tx, "acl-policies")
   792  
   793  	return idx, policy, nil
   794  }
   795  
   796  func (s *Store) ACLPolicyList(ws memdb.WatchSet) (uint64, structs.ACLPolicies, error) {
   797  	tx := s.db.Txn(false)
   798  	defer tx.Abort()
   799  
   800  	iter, err := tx.Get("acl-policies", "id")
   801  	if err != nil {
   802  		return 0, nil, fmt.Errorf("failed acl policy lookup: %v", err)
   803  	}
   804  	ws.Add(iter.WatchCh())
   805  
   806  	var result structs.ACLPolicies
   807  	for policy := iter.Next(); policy != nil; policy = iter.Next() {
   808  		result = append(result, policy.(*structs.ACLPolicy))
   809  	}
   810  
   811  	// Get the table index.
   812  	idx := maxIndexTxn(tx, "acl-policies")
   813  
   814  	return idx, result, nil
   815  }
   816  
   817  func (s *Store) ACLPolicyDeleteByID(idx uint64, id string) error {
   818  	return s.aclPolicyDelete(idx, id, "id")
   819  }
   820  
   821  func (s *Store) ACLPolicyDeleteByName(idx uint64, name string) error {
   822  	return s.aclPolicyDelete(idx, name, "name")
   823  }
   824  
   825  func (s *Store) ACLPolicyBatchDelete(idx uint64, policyIDs []string) error {
   826  	tx := s.db.Txn(true)
   827  	defer tx.Abort()
   828  
   829  	for _, policyID := range policyIDs {
   830  		if err := s.aclPolicyDeleteTxn(tx, idx, policyID, "id"); err != nil {
   831  			return err
   832  		}
   833  	}
   834  
   835  	if err := indexUpdateMaxTxn(tx, idx, "acl-policies"); err != nil {
   836  		return fmt.Errorf("failed updating index: %v", err)
   837  	}
   838  	tx.Commit()
   839  	return nil
   840  }
   841  
   842  func (s *Store) aclPolicyDelete(idx uint64, value, index string) error {
   843  	tx := s.db.Txn(true)
   844  	defer tx.Abort()
   845  
   846  	if err := s.aclPolicyDeleteTxn(tx, idx, value, index); err != nil {
   847  		return err
   848  	}
   849  	if err := indexUpdateMaxTxn(tx, idx, "acl-policies"); err != nil {
   850  		return fmt.Errorf("failed updating index: %v", err)
   851  	}
   852  
   853  	tx.Commit()
   854  	return nil
   855  }
   856  
   857  func (s *Store) aclPolicyDeleteTxn(tx *memdb.Txn, idx uint64, value, index string) error {
   858  	// Look up the existing token
   859  	rawPolicy, err := tx.First("acl-policies", index, value)
   860  	if err != nil {
   861  		return fmt.Errorf("failed acl policy lookup: %v", err)
   862  	}
   863  
   864  	if rawPolicy == nil {
   865  		return nil
   866  	}
   867  
   868  	policy := rawPolicy.(*structs.ACLPolicy)
   869  
   870  	if policy.ID == structs.ACLPolicyGlobalManagementID {
   871  		return fmt.Errorf("Deletion of the builtin global-management policy is not permitted")
   872  	}
   873  
   874  	if err := tx.Delete("acl-policies", policy); err != nil {
   875  		return fmt.Errorf("failed deleting acl policy: %v", err)
   876  	}
   877  	return nil
   878  }