diff --git a/acl/policy.go b/acl/policy.go index 3016869bdb..541f8bfd28 100644 --- a/acl/policy.go +++ b/acl/policy.go @@ -288,11 +288,10 @@ func (pr *PolicyRules) Validate(conf *EnterpriseACLConfig) error { return nil } -func parseCurrent(rules string, conf *EnterpriseACLConfig) (*Policy, error) { - p := &Policy{} - - if err := hcl.Decode(p, rules); err != nil { - return nil, fmt.Errorf("Failed to parse ACL rules: %v", err) +func parseCurrent(rules string, conf *EnterpriseACLConfig, meta *EnterprisePolicyMeta) (*Policy, error) { + p, err := decodeRules(rules, conf, meta) + if err != nil { + return nil, err } if err := p.PolicyRules.Validate(conf); err != nil { @@ -300,7 +299,7 @@ func parseCurrent(rules string, conf *EnterpriseACLConfig) (*Policy, error) { } if err := p.EnterprisePolicyRules.Validate(conf); err != nil { - return nil, fmt.Errorf("Invalidate enterprise rules: %v", err) + return nil, err } return p, nil @@ -423,7 +422,7 @@ func parseLegacy(rules string, conf *EnterpriseACLConfig) (*Policy, error) { // NewPolicyFromSource is used to parse the specified ACL rules into an // intermediary set of policies, before being compiled into // the ACL -func NewPolicyFromSource(id string, revision uint64, rules string, syntax SyntaxVersion, conf *EnterpriseACLConfig) (*Policy, error) { +func NewPolicyFromSource(id string, revision uint64, rules string, syntax SyntaxVersion, conf *EnterpriseACLConfig, meta *EnterprisePolicyMeta) (*Policy, error) { if rules == "" { // Hot path for empty source return &Policy{ID: id, Revision: revision}, nil @@ -435,7 +434,7 @@ func NewPolicyFromSource(id string, revision uint64, rules string, syntax Syntax case SyntaxLegacy: policy, err = parseLegacy(rules, conf) case SyntaxCurrent: - policy, err = parseCurrent(rules, conf) + policy, err = parseCurrent(rules, conf, meta) default: return nil, fmt.Errorf("Invalid rules version: %d", syntax) } diff --git a/acl/policy_oss.go b/acl/policy_oss.go index 247e6dcaa8..950867ccf2 100644 --- a/acl/policy_oss.go +++ b/acl/policy_oss.go @@ -2,6 +2,15 @@ package acl +import ( + "fmt" + + "github.com/hashicorp/hcl" +) + +// EnterprisePolicyMeta stub +type EnterprisePolicyMeta struct{} + // EnterpriseRule stub type EnterpriseRule struct{} @@ -17,3 +26,13 @@ func (r *EnterprisePolicyRules) Validate(*EnterpriseACLConfig) error { // nothing to validate return nil } + +func decodeRules(rules string, _ *EnterpriseACLConfig, _ *EnterprisePolicyMeta) (*Policy, error) { + p := &Policy{} + + if err := hcl.Decode(p, rules); err != nil { + return nil, fmt.Errorf("Failed to parse ACL rules: %v", err) + } + + return p, nil +} diff --git a/acl/policy_test.go b/acl/policy_test.go index 234506a581..2f8bbca0ce 100644 --- a/acl/policy_test.go +++ b/acl/policy_test.go @@ -544,7 +544,7 @@ func TestPolicySourceParse(t *testing.T) { for _, tc := range cases { t.Run(tc.Name, func(t *testing.T) { req := require.New(t) - actual, err := NewPolicyFromSource("", 0, tc.Rules, tc.Syntax, nil) + actual, err := NewPolicyFromSource("", 0, tc.Rules, tc.Syntax, nil, nil) if tc.Err != "" { errStartsWith(t, err, tc.Err) } else { diff --git a/agent/acl_endpoint.go b/agent/acl_endpoint.go index 22f26e3307..56a111e721 100644 --- a/agent/acl_endpoint.go +++ b/agent/acl_endpoint.go @@ -187,6 +187,7 @@ func (s *HTTPServer) ACLPolicyList(resp http.ResponseWriter, req *http.Request) if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { return nil, nil } + s.parseEntMeta(req, &args.EnterpriseMeta) if args.Datacenter == "" { args.Datacenter = s.agent.config.Datacenter @@ -244,6 +245,8 @@ func (s *HTTPServer) ACLPolicyRead(resp http.ResponseWriter, req *http.Request, return nil, nil } + s.parseEntMeta(req, &args.EnterpriseMeta) + if args.Datacenter == "" { args.Datacenter = s.agent.config.Datacenter } @@ -326,6 +329,7 @@ func (s *HTTPServer) aclPolicyWriteInternal(resp http.ResponseWriter, req *http. Datacenter: s.agent.config.Datacenter, } s.parseToken(req, &args.Token) + s.parseEntMeta(req, &args.Policy.EnterpriseMeta) if err := decodeBody(req, &args.Policy, fixTimeAndHashFields); err != nil { return nil, BadRequestError{Reason: fmt.Sprintf("Policy decoding failed: %v", err)} @@ -359,6 +363,7 @@ func (s *HTTPServer) ACLPolicyDelete(resp http.ResponseWriter, req *http.Request PolicyID: policyID, } s.parseToken(req, &args.Token) + s.parseEntMeta(req, &args.EnterpriseMeta) var ignored string if err := s.agent.RPC("ACL.PolicyDelete", args, &ignored); err != nil { @@ -381,6 +386,8 @@ func (s *HTTPServer) ACLTokenList(resp http.ResponseWriter, req *http.Request) ( return nil, nil } + s.parseEntMeta(req, &args.EnterpriseMeta) + if args.Datacenter == "" { args.Datacenter = s.agent.config.Datacenter } @@ -483,6 +490,8 @@ func (s *HTTPServer) ACLTokenGet(resp http.ResponseWriter, req *http.Request, to return nil, nil } + s.parseEntMeta(req, &args.EnterpriseMeta) + if args.Datacenter == "" { args.Datacenter = s.agent.config.Datacenter } @@ -510,6 +519,7 @@ func (s *HTTPServer) aclTokenSetInternal(resp http.ResponseWriter, req *http.Req Create: create, } s.parseToken(req, &args.Token) + s.parseEntMeta(req, &args.ACLToken.EnterpriseMeta) if err := decodeBody(req, &args.ACLToken, fixTimeAndHashFields); err != nil { return nil, BadRequestError{Reason: fmt.Sprintf("Token decoding failed: %v", err)} @@ -537,6 +547,7 @@ func (s *HTTPServer) ACLTokenDelete(resp http.ResponseWriter, req *http.Request, TokenID: tokenID, } s.parseToken(req, &args.Token) + s.parseEntMeta(req, &args.EnterpriseMeta) var ignored string if err := s.agent.RPC("ACL.TokenDelete", args, &ignored); err != nil { @@ -555,6 +566,8 @@ func (s *HTTPServer) ACLTokenClone(resp http.ResponseWriter, req *http.Request, Create: true, } + s.parseEntMeta(req, &args.ACLToken.EnterpriseMeta) + if err := decodeBody(req, &args.ACLToken, fixTimeAndHashFields); err != nil && err.Error() != "EOF" { return nil, BadRequestError{Reason: fmt.Sprintf("Token decoding failed: %v", err)} } @@ -580,6 +593,7 @@ func (s *HTTPServer) ACLRoleList(resp http.ResponseWriter, req *http.Request) (i if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { return nil, nil } + s.parseEntMeta(req, &args.EnterpriseMeta) if args.Datacenter == "" { args.Datacenter = s.agent.config.Datacenter @@ -656,6 +670,7 @@ func (s *HTTPServer) ACLRoleRead(resp http.ResponseWriter, req *http.Request, ro if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { return nil, nil } + s.parseEntMeta(req, &args.EnterpriseMeta) if args.Datacenter == "" { args.Datacenter = s.agent.config.Datacenter @@ -688,6 +703,7 @@ func (s *HTTPServer) ACLRoleWrite(resp http.ResponseWriter, req *http.Request, r Datacenter: s.agent.config.Datacenter, } s.parseToken(req, &args.Token) + s.parseEntMeta(req, &args.Role.EnterpriseMeta) if err := decodeBody(req, &args.Role, fixTimeAndHashFields); err != nil { return nil, BadRequestError{Reason: fmt.Sprintf("Role decoding failed: %v", err)} @@ -713,6 +729,7 @@ func (s *HTTPServer) ACLRoleDelete(resp http.ResponseWriter, req *http.Request, RoleID: roleID, } s.parseToken(req, &args.Token) + s.parseEntMeta(req, &args.EnterpriseMeta) var ignored string if err := s.agent.RPC("ACL.RoleDelete", args, &ignored); err != nil { @@ -732,6 +749,8 @@ func (s *HTTPServer) ACLBindingRuleList(resp http.ResponseWriter, req *http.Requ return nil, nil } + s.parseEntMeta(req, &args.EnterpriseMeta) + if args.Datacenter == "" { args.Datacenter = s.agent.config.Datacenter } @@ -790,6 +809,8 @@ func (s *HTTPServer) ACLBindingRuleRead(resp http.ResponseWriter, req *http.Requ return nil, nil } + s.parseEntMeta(req, &args.EnterpriseMeta) + if args.Datacenter == "" { args.Datacenter = s.agent.config.Datacenter } @@ -821,6 +842,7 @@ func (s *HTTPServer) ACLBindingRuleWrite(resp http.ResponseWriter, req *http.Req Datacenter: s.agent.config.Datacenter, } s.parseToken(req, &args.Token) + s.parseEntMeta(req, &args.BindingRule.EnterpriseMeta) if err := decodeBody(req, &args.BindingRule, fixTimeAndHashFields); err != nil { return nil, BadRequestError{Reason: fmt.Sprintf("BindingRule decoding failed: %v", err)} @@ -846,6 +868,7 @@ func (s *HTTPServer) ACLBindingRuleDelete(resp http.ResponseWriter, req *http.Re BindingRuleID: bindingRuleID, } s.parseToken(req, &args.Token) + s.parseEntMeta(req, &args.EnterpriseMeta) var ignored bool if err := s.agent.RPC("ACL.BindingRuleDelete", args, &ignored); err != nil { @@ -864,6 +887,7 @@ func (s *HTTPServer) ACLAuthMethodList(resp http.ResponseWriter, req *http.Reque if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { return nil, nil } + s.parseEntMeta(req, &args.EnterpriseMeta) if args.Datacenter == "" { args.Datacenter = s.agent.config.Datacenter @@ -920,6 +944,7 @@ func (s *HTTPServer) ACLAuthMethodRead(resp http.ResponseWriter, req *http.Reque if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done { return nil, nil } + s.parseEntMeta(req, &args.EnterpriseMeta) if args.Datacenter == "" { args.Datacenter = s.agent.config.Datacenter @@ -953,6 +978,7 @@ func (s *HTTPServer) ACLAuthMethodWrite(resp http.ResponseWriter, req *http.Requ Datacenter: s.agent.config.Datacenter, } s.parseToken(req, &args.Token) + s.parseEntMeta(req, &args.AuthMethod.EnterpriseMeta) if err := decodeBody(req, &args.AuthMethod, fixTimeAndHashFields); err != nil { return nil, BadRequestError{Reason: fmt.Sprintf("AuthMethod decoding failed: %v", err)} @@ -981,6 +1007,7 @@ func (s *HTTPServer) ACLAuthMethodDelete(resp http.ResponseWriter, req *http.Req AuthMethodName: methodName, } s.parseToken(req, &args.Token) + s.parseEntMeta(req, &args.EnterpriseMeta) var ignored bool if err := s.agent.RPC("ACL.AuthMethodDelete", args, &ignored); err != nil { @@ -997,8 +1024,10 @@ func (s *HTTPServer) ACLLogin(resp http.ResponseWriter, req *http.Request) (inte args := &structs.ACLLoginRequest{ Datacenter: s.agent.config.Datacenter, + Auth: &structs.ACLLoginParams{}, } s.parseDC(req, &args.Datacenter) + s.parseEntMeta(req, &args.Auth.EnterpriseMeta) if err := decodeBody(req, &args.Auth, nil); err != nil { return nil, BadRequestError{Reason: fmt.Sprintf("Failed to decode request body:: %v", err)} diff --git a/agent/agentpb/encoder.go b/agent/agentpb/encoder.go deleted file mode 100644 index 1792993b7f..0000000000 --- a/agent/agentpb/encoder.go +++ /dev/null @@ -1,35 +0,0 @@ -package agentpb - -import ( - "fmt" - - "github.com/hashicorp/consul/agent/structs" -) - -type ProtoMarshaller interface { - Size() int - MarshalTo([]byte) (int, error) - Unmarshal([]byte) error - ProtoMessage() -} - -func EncodeInterface(t structs.MessageType, message interface{}) ([]byte, error) { - if marshaller, ok := message.(ProtoMarshaller); ok { - return Encode(t, marshaller) - } - return nil, fmt.Errorf("message does not implement the ProtoMarshaller interface: %T", message) -} - -func Encode(t structs.MessageType, message ProtoMarshaller) ([]byte, error) { - data := make([]byte, message.Size()+1) - data[0] = uint8(t) - if _, err := message.MarshalTo(data[1:]); err != nil { - return nil, err - } - return data, nil -} - -func Decode(buf []byte, out ProtoMarshaller) error { - // Note that this assumes the leading byte indicating the type has already been stripped off - return out.Unmarshal(buf) -} diff --git a/agent/consul/acl.go b/agent/consul/acl.go index ae3fefb8b8..74ac143143 100644 --- a/agent/consul/acl.go +++ b/agent/consul/acl.go @@ -608,7 +608,7 @@ func (r *ACLResolver) resolvePoliciesForIdentity(identity structs.ACLIdentity) ( serviceIdentities = dedupeServiceIdentities(serviceIdentities) // Generate synthetic policies for all service identities in effect. - syntheticPolicies := r.synthesizePoliciesForServiceIdentities(serviceIdentities) + syntheticPolicies := r.synthesizePoliciesForServiceIdentities(serviceIdentities, identity.EnterpriseMetadata()) // For the new ACLs policy replication is mandatory for correct operation on servers. Therefore // we only attempt to resolve policies locally @@ -622,14 +622,14 @@ func (r *ACLResolver) resolvePoliciesForIdentity(identity structs.ACLIdentity) ( return filtered, nil } -func (r *ACLResolver) synthesizePoliciesForServiceIdentities(serviceIdentities []*structs.ACLServiceIdentity) []*structs.ACLPolicy { +func (r *ACLResolver) synthesizePoliciesForServiceIdentities(serviceIdentities []*structs.ACLServiceIdentity, entMeta *structs.EnterpriseMeta) []*structs.ACLPolicy { if len(serviceIdentities) == 0 { return nil } syntheticPolicies := make([]*structs.ACLPolicy, 0, len(serviceIdentities)) for _, s := range serviceIdentities { - syntheticPolicies = append(syntheticPolicies, s.SyntheticPolicy()) + syntheticPolicies = append(syntheticPolicies, s.SyntheticPolicy(entMeta)) } return syntheticPolicies @@ -1355,26 +1355,166 @@ func (f *aclFilter) filterPreparedQueries(queries *structs.PreparedQueries) { *queries = ret } -func (f *aclFilter) redactTokenSecret(token **structs.ACLToken) { - // TODO (namespaces) update to call with an actual ent authz context once acls support it - if token == nil || *token == nil || f == nil || f.authorizer.ACLWrite(nil) == acl.Allow { +func (f *aclFilter) filterToken(token **structs.ACLToken) { + var entCtx acl.EnterpriseAuthorizerContext + if token == nil || *token == nil || f == nil { return } - clone := *(*token) - clone.SecretID = redactedToken - *token = &clone + + (*token).FillAuthzContext(&entCtx) + + if f.authorizer.ACLRead(&entCtx) != acl.Allow { + // no permissions to read + *token = nil + } else if f.authorizer.ACLWrite(&entCtx) != acl.Allow { + // no write permissions - redact secret + clone := *(*token) + clone.SecretID = redactedToken + *token = &clone + } } -func (f *aclFilter) redactTokenSecrets(tokens *structs.ACLTokens) { +func (f *aclFilter) filterTokens(tokens *structs.ACLTokens) { ret := make(structs.ACLTokens, 0, len(*tokens)) for _, token := range *tokens { final := token - f.redactTokenSecret(&final) - ret = append(ret, final) + f.filterToken(&final) + if final != nil { + ret = append(ret, final) + } } *tokens = ret } +func (f *aclFilter) filterTokenStub(token **structs.ACLTokenListStub) { + var entCtx acl.EnterpriseAuthorizerContext + if token == nil || *token == nil || f == nil { + return + } + + (*token).FillAuthzContext(&entCtx) + + if f.authorizer.ACLRead(&entCtx) != acl.Allow { + *token = nil + } +} + +func (f *aclFilter) filterTokenStubs(tokens *[]*structs.ACLTokenListStub) { + ret := make(structs.ACLTokenListStubs, 0, len(*tokens)) + for _, token := range *tokens { + final := token + f.filterTokenStub(&final) + if final != nil { + ret = append(ret, final) + } + } + *tokens = ret +} + +func (f *aclFilter) filterPolicy(policy **structs.ACLPolicy) { + var entCtx acl.EnterpriseAuthorizerContext + if policy == nil || *policy == nil || f == nil { + return + } + + (*policy).FillAuthzContext(&entCtx) + + if f.authorizer.ACLRead(&entCtx) != acl.Allow { + // no permissions to read + *policy = nil + } +} + +func (f *aclFilter) filterPolicies(policies *structs.ACLPolicies) { + ret := make(structs.ACLPolicies, 0, len(*policies)) + for _, policy := range *policies { + final := policy + f.filterPolicy(&final) + if final != nil { + ret = append(ret, final) + } + } + *policies = ret +} + +func (f *aclFilter) filterRole(role **structs.ACLRole) { + var entCtx acl.EnterpriseAuthorizerContext + if role == nil || *role == nil || f == nil { + return + } + + (*role).FillAuthzContext(&entCtx) + + if f.authorizer.ACLRead(&entCtx) != acl.Allow { + // no permissions to read + *role = nil + } +} + +func (f *aclFilter) filterRoles(roles *structs.ACLRoles) { + ret := make(structs.ACLRoles, 0, len(*roles)) + for _, role := range *roles { + final := role + f.filterRole(&final) + if final != nil { + ret = append(ret, final) + } + } + *roles = ret +} + +func (f *aclFilter) filterBindingRule(rule **structs.ACLBindingRule) { + var entCtx acl.EnterpriseAuthorizerContext + if rule == nil || *rule == nil || f == nil { + return + } + + (*rule).FillAuthzContext(&entCtx) + + if f.authorizer.ACLRead(&entCtx) != acl.Allow { + // no permissions to read + *rule = nil + } +} + +func (f *aclFilter) filterBindingRules(rules *structs.ACLBindingRules) { + ret := make(structs.ACLBindingRules, 0, len(*rules)) + for _, rule := range *rules { + final := rule + f.filterBindingRule(&final) + if final != nil { + ret = append(ret, final) + } + } + *rules = ret +} + +func (f *aclFilter) filterAuthMethod(method **structs.ACLAuthMethod) { + var entCtx acl.EnterpriseAuthorizerContext + if method == nil || *method == nil || f == nil { + return + } + + (*method).FillAuthzContext(&entCtx) + + if f.authorizer.ACLRead(&entCtx) != acl.Allow { + // no permissions to read + *method = nil + } +} + +func (f *aclFilter) filterAuthMethods(methods *structs.ACLAuthMethods) { + ret := make(structs.ACLAuthMethods, 0, len(*methods)) + for _, method := range *methods { + final := method + f.filterAuthMethod(&final) + if final != nil { + ret = append(ret, final) + } + } + *methods = ret +} + func (r *ACLResolver) filterACLWithAuthorizer(authorizer acl.Authorizer, subj interface{}) error { if authorizer == nil { return nil @@ -1423,13 +1563,36 @@ func (r *ACLResolver) filterACLWithAuthorizer(authorizer acl.Authorizer, subj in filt.redactPreparedQueryTokens(v) case *structs.ACLTokens: - filt.redactTokenSecrets(v) - + filt.filterTokens(v) case **structs.ACLToken: - filt.redactTokenSecret(v) + filt.filterToken(v) + case *[]*structs.ACLTokenListStub: + filt.filterTokenStubs(v) + case **structs.ACLTokenListStub: + filt.filterTokenStub(v) + + case *structs.ACLPolicies: + filt.filterPolicies(v) + case **structs.ACLPolicy: + filt.filterPolicy(v) + + case *structs.ACLRoles: + filt.filterRoles(v) + case **structs.ACLRole: + filt.filterRole(v) + + case *structs.ACLBindingRules: + filt.filterBindingRules(v) + case **structs.ACLBindingRule: + filt.filterBindingRule(v) + + case *structs.ACLAuthMethods: + filt.filterAuthMethods(v) + case **structs.ACLAuthMethod: + filt.filterAuthMethod(v) default: - panic(fmt.Errorf("Unhandled type passed to ACL filter: %#v", subj)) + panic(fmt.Errorf("Unhandled type passed to ACL filter: %T %#v", subj, subj)) } return nil diff --git a/agent/consul/acl_authmethod.go b/agent/consul/acl_authmethod.go index 3c5d21aaea..7d709f4ba0 100644 --- a/agent/consul/acl_authmethod.go +++ b/agent/consul/acl_authmethod.go @@ -21,7 +21,7 @@ type authMethodValidatorEntry struct { // then the cached version is returned, otherwise a new validator is created // and cached. func (s *Server) loadAuthMethodValidator(idx uint64, method *structs.ACLAuthMethod) (authmethod.Validator, error) { - if prevIdx, v, ok := s.getCachedAuthMethodValidator(method.Name); ok && idx <= prevIdx { + if prevIdx, v, ok := s.aclAuthMethodValidators.GetValidator(method); ok && idx <= prevIdx { return v, nil } @@ -30,61 +30,11 @@ func (s *Server) loadAuthMethodValidator(idx uint64, method *structs.ACLAuthMeth return nil, fmt.Errorf("auth method validator for %q could not be initialized: %v", method.Name, err) } - v = s.getOrReplaceAuthMethodValidator(method.Name, idx, v) + v = s.aclAuthMethodValidators.PutValidatorIfNewer(method, v, idx) return v, nil } -// getCachedAuthMethodValidator returns an AuthMethodValidator for -// the given name exclusively from the cache. If one is not found in the cache -// nil is returned. -func (s *Server) getCachedAuthMethodValidator(name string) (uint64, authmethod.Validator, bool) { - s.aclAuthMethodValidatorLock.RLock() - defer s.aclAuthMethodValidatorLock.RUnlock() - - if s.aclAuthMethodValidators != nil { - v, ok := s.aclAuthMethodValidators[name] - if ok { - return v.ModifyIndex, v.Validator, true - } - } - return 0, nil, false -} - -// getOrReplaceAuthMethodValidator updates the cached validator with the -// provided one UNLESS it has been updated by another goroutine in which case -// the updated one is returned. -func (s *Server) getOrReplaceAuthMethodValidator(name string, idx uint64, v authmethod.Validator) authmethod.Validator { - s.aclAuthMethodValidatorLock.Lock() - defer s.aclAuthMethodValidatorLock.Unlock() - - if s.aclAuthMethodValidators == nil { - s.aclAuthMethodValidators = make(map[string]*authMethodValidatorEntry) - } - - prev, ok := s.aclAuthMethodValidators[name] - if ok { - if prev.ModifyIndex >= idx { - return prev.Validator - } - } - - s.logger.Printf("[DEBUG] acl: updating cached auth method validator for %q", name) - - s.aclAuthMethodValidators[name] = &authMethodValidatorEntry{ - Validator: v, - ModifyIndex: idx, - } - return v -} - -// purgeAuthMethodValidators resets the cache of validators. -func (s *Server) purgeAuthMethodValidators() { - s.aclAuthMethodValidatorLock.Lock() - s.aclAuthMethodValidators = make(map[string]*authMethodValidatorEntry) - s.aclAuthMethodValidatorLock.Unlock() -} - // evaluateRoleBindings evaluates all current binding rules associated with the // given auth method against the verified data returned from the authentication // process. @@ -93,9 +43,10 @@ func (s *Server) purgeAuthMethodValidators() { func (s *Server) evaluateRoleBindings( validator authmethod.Validator, verifiedFields map[string]string, + entMeta *structs.EnterpriseMeta, ) ([]*structs.ACLServiceIdentity, []structs.ACLTokenRoleLink, error) { // Only fetch rules that are relevant for this method. - _, rules, err := s.fsm.State().ACLBindingRuleList(nil, validator.Name()) + _, rules, err := s.fsm.State().ACLBindingRuleList(nil, validator.Name(), entMeta) if err != nil { return nil, nil, err } else if len(rules) == 0 { @@ -136,7 +87,7 @@ func (s *Server) evaluateRoleBindings( }) case structs.BindingRuleBindTypeRole: - _, role, err := s.fsm.State().ACLRoleGetByName(nil, bindName) + _, role, err := s.fsm.State().ACLRoleGetByName(nil, bindName, &rule.EnterpriseMeta) if err != nil { return nil, nil, err } diff --git a/agent/consul/acl_endpoint.go b/agent/consul/acl_endpoint.go index 0ba4b02ab7..7ac827e47c 100644 --- a/agent/consul/acl_endpoint.go +++ b/agent/consul/acl_endpoint.go @@ -166,6 +166,8 @@ func (a *ACL) BootstrapTokens(args *structs.DCSpecificRequest, reply *structs.AC ResetIndex: specifiedIndex, } + req.Token.EnterpriseMeta.InitDefault() + req.Token.SetHash(true) resp, err := a.srv.raftApply(structs.ACLBootstrapRequestType, &req) @@ -177,7 +179,7 @@ func (a *ACL) BootstrapTokens(args *structs.DCSpecificRequest, reply *structs.AC return err } - if _, token, err := state.ACLTokenGetByAccessor(nil, accessor); err == nil { + if _, token, err := state.ACLTokenGetByAccessor(nil, accessor, structs.DefaultEnterpriseMeta()); err == nil { *reply = *token } @@ -201,15 +203,18 @@ func (a *ACL) TokenRead(args *structs.ACLTokenGetRequest, reply *structs.ACLToke } var rule acl.Authorizer + if args.TokenIDType == structs.ACLTokenAccessor { + var entCtx acl.EnterpriseAuthorizerContext + args.FillAuthzContext(&entCtx) + var err error // Only ACLRead privileges are required to list tokens // However if you do not have ACLWrite as well the token // secrets will be redacted - // TODO (namespaces) update to call ACLRead with an authz context once ACLs support it if rule, err = a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLRead(nil) != acl.Allow { + } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } } @@ -221,16 +226,18 @@ func (a *ACL) TokenRead(args *structs.ACLTokenGetRequest, reply *structs.ACLToke var err error if args.TokenIDType == structs.ACLTokenAccessor { - index, token, err = state.ACLTokenGetByAccessor(ws, args.TokenID) + index, token, err = state.ACLTokenGetByAccessor(ws, args.TokenID, &args.EnterpriseMeta) if token != nil { a.srv.filterACLWithAuthorizer(rule, &token) - // TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it - if rule.ACLWrite(nil) != acl.Allow { + + // token secret was redacted + if token.SecretID == redactedToken { reply.Redacted = true } } } else { - index, token, err = state.ACLTokenGetBySecret(ws, args.TokenID) + index, token, err = state.ACLTokenGetBySecret(ws, args.TokenID, nil) + // no extra validation is needed here. If you have the secret ID you can read it. } if token != nil && token.IsExpired(time.Now()) { @@ -263,14 +270,16 @@ func (a *ACL) TokenClone(args *structs.ACLTokenSetRequest, reply *structs.ACLTok defer metrics.MeasureSince([]string{"acl", "token", "clone"}, time.Now()) - // TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it + var entCtx acl.EnterpriseAuthorizerContext + args.ACLToken.FillAuthzContext(&entCtx) + if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLWrite(nil) != acl.Allow { + } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } - _, token, err := a.srv.fsm.State().ACLTokenGetByAccessor(nil, args.ACLToken.AccessorID) + _, token, err := a.srv.fsm.State().ACLTokenGetByAccessor(nil, args.ACLToken.AccessorID, &args.ACLToken.EnterpriseMeta) if err != nil { return err } else if token == nil || token.IsExpired(time.Now()) { @@ -297,6 +306,7 @@ func (a *ACL) TokenClone(args *structs.ACLTokenSetRequest, reply *structs.ACLTok Local: token.Local, Description: token.Description, ExpirationTime: token.ExpirationTime, + EnterpriseMeta: args.ACLToken.EnterpriseMeta, }, WriteRequest: args.WriteRequest, } @@ -327,10 +337,11 @@ func (a *ACL) TokenSet(args *structs.ACLTokenSetRequest, reply *structs.ACLToken defer metrics.MeasureSince([]string{"acl", "token", "upsert"}, time.Now()) // Verify token is permitted to modify ACLs - // TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it + var entCtx acl.EnterpriseAuthorizerContext + args.ACLToken.FillAuthzContext(&entCtx) if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLWrite(nil) != acl.Allow { + } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } @@ -354,13 +365,13 @@ func (a *ACL) tokenSetInternal(args *structs.ACLTokenSetRequest, reply *structs. var err error if token.AccessorID != "" { - _, accessorMatch, err = state.ACLTokenGetByAccessor(nil, token.AccessorID) + _, accessorMatch, err = state.ACLTokenGetByAccessor(nil, token.AccessorID, nil) if err != nil { return fmt.Errorf("Failed acl token lookup by accessor: %v", err) } } if token.SecretID != "" { - _, secretMatch, err = state.ACLTokenGetBySecret(nil, token.SecretID) + _, secretMatch, err = state.ACLTokenGetBySecret(nil, token.SecretID, nil) if err != nil { return fmt.Errorf("Failed acl token lookup by secret: %v", err) } @@ -379,7 +390,7 @@ func (a *ACL) tokenSetInternal(args *structs.ACLTokenSetRequest, reply *structs. return fmt.Errorf("Invalid Token: AccessorID is not a valid UUID") } else if accessorMatch != nil { return fmt.Errorf("Invalid Token: AccessorID is already in use") - } else if _, match, err := state.ACLTokenGetBySecret(nil, token.AccessorID); err != nil || match != nil { + } else if _, match, err := state.ACLTokenGetBySecret(nil, token.AccessorID, nil); err != nil || match != nil { if err != nil { return fmt.Errorf("Failed to lookup the acl token: %v", err) } @@ -388,7 +399,7 @@ func (a *ACL) tokenSetInternal(args *structs.ACLTokenSetRequest, reply *structs. return fmt.Errorf("Invalid Token: UUIDs with the prefix %q are reserved", structs.ACLReservedPrefix) } - // Generate the AccessorID if not specified + // Generate the SecretID if not specified if token.SecretID == "" { token.SecretID, err = lib.GenerateUUID(a.srv.checkTokenUUID) if err != nil { @@ -398,7 +409,7 @@ func (a *ACL) tokenSetInternal(args *structs.ACLTokenSetRequest, reply *structs. return fmt.Errorf("Invalid Token: SecretID is not a valid UUID") } else if secretMatch != nil { return fmt.Errorf("Invalid Token: SecretID is already in use") - } else if _, match, err := state.ACLTokenGetByAccessor(nil, token.SecretID); err != nil || match != nil { + } else if _, match, err := state.ACLTokenGetByAccessor(nil, token.SecretID, nil); err != nil || match != nil { if err != nil { return fmt.Errorf("Failed to lookup the acl token: %v", err) } @@ -509,7 +520,7 @@ func (a *ACL) tokenSetInternal(args *structs.ACLTokenSetRequest, reply *structs. // Validate all the policy names and convert them to policy IDs for _, link := range token.Policies { if link.ID == "" { - _, policy, err := state.ACLPolicyGetByName(nil, link.Name) + _, policy, err := state.ACLPolicyGetByName(nil, link.Name, &token.EnterpriseMeta) if err != nil { return fmt.Errorf("Error looking up policy for name %q: %v", link.Name, err) } @@ -517,6 +528,15 @@ func (a *ACL) tokenSetInternal(args *structs.ACLTokenSetRequest, reply *structs. return fmt.Errorf("No such ACL policy with name %q", link.Name) } link.ID = policy.ID + } else { + _, policy, err := state.ACLPolicyGetByID(nil, link.ID, &token.EnterpriseMeta) + if err != nil { + return fmt.Errorf("Error looking up policy for id %q: %v", link.ID, err) + } + + if policy == nil { + return fmt.Errorf("No such ACL policy with ID %q", link.ID) + } } // Do not store the policy name within raft/memdb as the policy could be renamed in the future. @@ -536,7 +556,7 @@ func (a *ACL) tokenSetInternal(args *structs.ACLTokenSetRequest, reply *structs. // Validate all the role names and convert them to role IDs. for _, link := range token.Roles { if link.ID == "" { - _, role, err := state.ACLRoleGetByName(nil, link.Name) + _, role, err := state.ACLRoleGetByName(nil, link.Name, &token.EnterpriseMeta) if err != nil { return fmt.Errorf("Error looking up role for name %q: %v", link.Name, err) } @@ -544,6 +564,15 @@ func (a *ACL) tokenSetInternal(args *structs.ACLTokenSetRequest, reply *structs. return fmt.Errorf("No such ACL role with name %q", link.Name) } link.ID = role.ID + } else { + _, role, err := state.ACLRoleGetByID(nil, link.ID, &token.EnterpriseMeta) + if err != nil { + return fmt.Errorf("Error looking up role for id %q: %v", link.ID, err) + } + + if role == nil { + return fmt.Errorf("No such ACL role with ID %q", link.ID) + } } // Do not store the role name within raft/memdb as the role could be renamed in the future. @@ -580,6 +609,12 @@ func (a *ACL) tokenSetInternal(args *structs.ACLTokenSetRequest, reply *structs. token.SetHash(true) + // validate the enterprise meta + err = state.ACLTokenUpsertValidateEnterprise(token, accessorMatch) + if err != nil { + return err + } + req := &structs.ACLTokenBatchSetRequest{ Tokens: structs.ACLTokens{token}, CAS: false, @@ -606,7 +641,7 @@ func (a *ACL) tokenSetInternal(args *structs.ACLTokenSetRequest, reply *structs. } // Don't check expiration times here as it doesn't really matter. - if _, updatedToken, err := a.srv.fsm.State().ACLTokenGetByAccessor(nil, token.AccessorID); err == nil && updatedToken != nil { + if _, updatedToken, err := a.srv.fsm.State().ACLTokenGetByAccessor(nil, token.AccessorID, nil); err == nil && updatedToken != nil { *reply = *updatedToken } else { return fmt.Errorf("Failed to retrieve the token after insertion") @@ -687,10 +722,12 @@ func (a *ACL) TokenDelete(args *structs.ACLTokenDeleteRequest, reply *string) er defer metrics.MeasureSince([]string{"acl", "token", "delete"}, time.Now()) // Verify token is permitted to modify ACLs - // TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it + var entCtx acl.EnterpriseAuthorizerContext + args.FillAuthzContext(&entCtx) + if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLWrite(nil) != acl.Allow { + } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } @@ -703,7 +740,7 @@ func (a *ACL) TokenDelete(args *structs.ACLTokenDeleteRequest, reply *string) er } // grab the token here so we can invalidate our cache later on - _, token, err := a.srv.fsm.State().ACLTokenGetByAccessor(nil, args.TokenID) + _, token, err := a.srv.fsm.State().ACLTokenGetByAccessor(nil, args.TokenID, &args.EnterpriseMeta) if err != nil { return err } @@ -715,10 +752,18 @@ func (a *ACL) TokenDelete(args *structs.ACLTokenDeleteRequest, reply *string) er // No need to check expiration time because it's being deleted. + // token found in secondary DC but its not local so it must be deleted in the primary if !a.srv.InACLDatacenter() && !token.Local { args.Datacenter = a.srv.config.ACLDatacenter return a.srv.forwardDC("ACL.TokenDelete", a.srv.config.ACLDatacenter, args, reply) } + } else if !a.srv.InACLDatacenter() { + // token not found in secondary DC - attempt to delete within the primary + args.Datacenter = a.srv.config.ACLDatacenter + return a.srv.forwardDC("ACL.TokenDelete", a.srv.config.ACLDatacenter, args, reply) + } else { + // in Primary Datacenter but the token does not exist - return early as there is nothing to do. + return nil } req := &structs.ACLTokenBatchDeleteRequest{ @@ -731,15 +776,13 @@ func (a *ACL) TokenDelete(args *structs.ACLTokenDeleteRequest, reply *string) er } // Purge the identity from the cache to prevent using the previous definition of the identity - if token != nil { - a.srv.acls.cache.RemoveIdentity(token.SecretID) - } + a.srv.acls.cache.RemoveIdentity(token.SecretID) if respErr, ok := resp.(error); ok { return respErr } - if reply != nil && token != nil { + if reply != nil { *reply = token.AccessorID } @@ -765,16 +808,15 @@ func (a *ACL) TokenList(args *structs.ACLTokenListRequest, reply *structs.ACLTok } rule, err := a.srv.ResolveToken(args.Token) - // TODO (namespaces) update to call ACLRead with an authz context once ACLs support it if err != nil { return err - } else if rule == nil || rule.ACLRead(nil) != acl.Allow { + } else if rule == nil { return acl.ErrPermissionDenied } return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta, func(ws memdb.WatchSet, state *state.Store) error { - index, tokens, err := state.ACLTokenList(ws, args.IncludeLocal, args.IncludeGlobal, args.Policy, args.Role, args.AuthMethod) + index, tokens, err := state.ACLTokenList(ws, args.IncludeLocal, args.IncludeGlobal, args.Policy, args.Role, args.AuthMethod, &args.EnterpriseMeta) if err != nil { return err } @@ -788,6 +830,12 @@ func (a *ACL) TokenList(args *structs.ACLTokenListRequest, reply *structs.ACLTok } stubs = append(stubs, token.Stub()) } + + // filter down to just the tokens that the requester has permissions to read + if err := a.srv.filterACLWithAuthorizer(rule, &stubs); err != nil { + return err + } + reply.Index, reply.Tokens = index, stubs return nil }) @@ -807,10 +855,9 @@ func (a *ACL) TokenBatchRead(args *structs.ACLTokenBatchGetRequest, reply *struc } rule, err := a.srv.ResolveToken(args.Token) - // TODO (namespaces) update to call ACLRead with an authz context once ACLs support it if err != nil { return err - } else if rule == nil || rule.ACLRead(nil) != acl.Allow { + } else if rule == nil { return acl.ErrPermissionDenied } @@ -823,11 +870,27 @@ func (a *ACL) TokenBatchRead(args *structs.ACLTokenBatchGetRequest, reply *struc // This RPC is used for replication, so don't filter out expired tokens here. - a.srv.filterACLWithAuthorizer(rule, &tokens) + // Filter the tokens down to just what we have permission to see - also redact + // secrets based on allowed permissions. We could just call filterACLWithAuthorizer + // on the whole token list but then it would require another pass through the token + // list to determine if any secrets were redacted. Its a small amount of code to + // process the loop so it was duplicated here and we instead call the filter func + // with just a single token. + ret := make(structs.ACLTokens, 0, len(tokens)) + for _, token := range tokens { + final := token + a.srv.filterACLWithAuthorizer(rule, &final) + if final != nil { + ret = append(ret, final) + if final.SecretID == redactedToken { + reply.Redacted = true + } + } else { + reply.Removed = true + } + } - reply.Index, reply.Tokens = index, tokens - // TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it - reply.Redacted = rule.ACLWrite(nil) != acl.Allow + reply.Index, reply.Tokens = index, ret return nil }) } @@ -841,15 +904,18 @@ func (a *ACL) PolicyRead(args *structs.ACLPolicyGetRequest, reply *structs.ACLPo return err } + var entCtx acl.EnterpriseAuthorizerContext + args.FillAuthzContext(&entCtx) + if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLRead(nil) != acl.Allow { + } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta, func(ws memdb.WatchSet, state *state.Store) error { - index, policy, err := state.ACLPolicyGetByID(ws, args.PolicyID) + index, policy, err := state.ACLPolicyGetByID(ws, args.PolicyID, &args.EnterpriseMeta) if err != nil { return err @@ -869,9 +935,10 @@ func (a *ACL) PolicyBatchRead(args *structs.ACLPolicyBatchGetRequest, reply *str return err } - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + rule, err := a.srv.ResolveToken(args.Token) + if err != nil { return err - } else if rule == nil || rule.ACLRead(nil) != acl.Allow { + } else if rule == nil { return acl.ErrPermissionDenied } @@ -882,6 +949,8 @@ func (a *ACL) PolicyBatchRead(args *structs.ACLPolicyBatchGetRequest, reply *str return err } + a.srv.filterACLWithAuthorizer(rule, &policies) + reply.Index, reply.Policies = index, policies return nil }) @@ -903,10 +972,12 @@ func (a *ACL) PolicySet(args *structs.ACLPolicySetRequest, reply *structs.ACLPol defer metrics.MeasureSince([]string{"acl", "policy", "upsert"}, time.Now()) // Verify token is permitted to modify ACLs - // TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it + var entCtx acl.EnterpriseAuthorizerContext + args.Policy.FillAuthzContext(&entCtx) + if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLWrite(nil) != acl.Allow { + } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } @@ -935,12 +1006,12 @@ func (a *ACL) PolicySet(args *structs.ACLPolicySetRequest, reply *structs.ACLPol return fmt.Errorf("Policy ID invalid UUID") } - _, idMatch, err = state.ACLPolicyGetByID(nil, policy.ID) + _, idMatch, err = state.ACLPolicyGetByID(nil, policy.ID, nil) if err != nil { return fmt.Errorf("acl policy lookup by id failed: %v", err) } } - _, nameMatch, err = state.ACLPolicyGetByName(nil, policy.Name) + _, nameMatch, err = state.ACLPolicyGetByName(nil, policy.Name, &policy.EnterpriseMeta) if err != nil { return fmt.Errorf("acl policy lookup by name failed: %v", err) } @@ -980,7 +1051,13 @@ func (a *ACL) PolicySet(args *structs.ACLPolicySetRequest, reply *structs.ACLPol } // validate the rules - _, err = acl.NewPolicyFromSource("", 0, policy.Rules, policy.Syntax, a.srv.enterpriseACLConfig) + _, err = acl.NewPolicyFromSource("", 0, policy.Rules, policy.Syntax, a.srv.enterpriseACLConfig, policy.EnterprisePolicyMeta()) + if err != nil { + return err + } + + // validate the enterprise meta + err = state.ACLPolicyUpsertValidateEnterprise(policy, idMatch) if err != nil { return err } @@ -1004,7 +1081,7 @@ func (a *ACL) PolicySet(args *structs.ACLPolicySetRequest, reply *structs.ACLPol return respErr } - if _, policy, err := a.srv.fsm.State().ACLPolicyGetByID(nil, policy.ID); err == nil && policy != nil { + if _, policy, err := a.srv.fsm.State().ACLPolicyGetByID(nil, policy.ID, &policy.EnterpriseMeta); err == nil && policy != nil { *reply = *policy } @@ -1027,14 +1104,16 @@ func (a *ACL) PolicyDelete(args *structs.ACLPolicyDeleteRequest, reply *string) defer metrics.MeasureSince([]string{"acl", "policy", "delete"}, time.Now()) // Verify token is permitted to modify ACLs - // TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it + var entCtx acl.EnterpriseAuthorizerContext + args.FillAuthzContext(&entCtx) + if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLWrite(nil) != acl.Allow { + } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } - _, policy, err := a.srv.fsm.State().ACLPolicyGetByID(nil, args.PolicyID) + _, policy, err := a.srv.fsm.State().ACLPolicyGetByID(nil, args.PolicyID, &args.EnterpriseMeta) if err != nil { return err } @@ -1078,20 +1157,23 @@ func (a *ACL) PolicyList(args *structs.ACLPolicyListRequest, reply *structs.ACLP return err } - // TODO (namespaces) update to call ACLRead with an authz context once ACLs support it - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + rule, err := a.srv.ResolveToken(args.Token) + if err != nil { return err - } else if rule == nil || rule.ACLRead(nil) != acl.Allow { + } else if rule == nil { return acl.ErrPermissionDenied } return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta, func(ws memdb.WatchSet, state *state.Store) error { - index, policies, err := state.ACLPolicyList(ws) + index, policies, err := state.ACLPolicyList(ws, &args.EnterpriseMeta) if err != nil { return err } + // filter down to just what the requester has permissions to see + a.srv.filterACLWithAuthorizer(rule, &policies) + var stubs structs.ACLPolicyListStubs for _, policy := range policies { stubs = append(stubs, policy.Stub()) @@ -1224,10 +1306,12 @@ func (a *ACL) RoleRead(args *structs.ACLRoleGetRequest, reply *structs.ACLRoleRe return err } - // TODO (namespaces) update to create and use actual enterprise authorizer context + var entCtx acl.EnterpriseAuthorizerContext + args.FillAuthzContext(&entCtx) + if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLRead(nil) != acl.Allow { + } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } @@ -1239,9 +1323,9 @@ func (a *ACL) RoleRead(args *structs.ACLRoleGetRequest, reply *structs.ACLRoleRe err error ) if args.RoleID != "" { - index, role, err = state.ACLRoleGetByID(ws, args.RoleID) + index, role, err = state.ACLRoleGetByID(ws, args.RoleID, &args.EnterpriseMeta) } else { - index, role, err = state.ACLRoleGetByName(ws, args.RoleName) + index, role, err = state.ACLRoleGetByName(ws, args.RoleName, &args.EnterpriseMeta) } if err != nil { @@ -1262,10 +1346,10 @@ func (a *ACL) RoleBatchRead(args *structs.ACLRoleBatchGetRequest, reply *structs return err } - // TODO (namespaces) update to create and use actual enterprise authorizer context - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + rule, err := a.srv.ResolveToken(args.Token) + if err != nil { return err - } else if rule == nil || rule.ACLRead(nil) != acl.Allow { + } else if rule == nil { return acl.ErrPermissionDenied } @@ -1276,6 +1360,8 @@ func (a *ACL) RoleBatchRead(args *structs.ACLRoleBatchGetRequest, reply *structs return err } + a.srv.filterACLWithAuthorizer(rule, &roles) + reply.Index, reply.Roles = index, roles return nil }) @@ -1297,10 +1383,12 @@ func (a *ACL) RoleSet(args *structs.ACLRoleSetRequest, reply *structs.ACLRole) e defer metrics.MeasureSince([]string{"acl", "role", "upsert"}, time.Now()) // Verify token is permitted to modify ACLs - // TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it + var entCtx acl.EnterpriseAuthorizerContext + args.Role.FillAuthzContext(&entCtx) + if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLWrite(nil) != acl.Allow { + } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } @@ -1330,7 +1418,7 @@ func (a *ACL) RoleSet(args *structs.ACLRoleSetRequest, reply *structs.ACLRole) e } // validate the name is unique - if _, existing, err := state.ACLRoleGetByName(nil, role.Name); err != nil { + if _, existing, err := state.ACLRoleGetByName(nil, role.Name, &role.EnterpriseMeta); err != nil { return fmt.Errorf("acl role lookup by name failed: %v", err) } else if existing != nil { return fmt.Errorf("Invalid Role: A Role with Name %q already exists", role.Name) @@ -1341,7 +1429,7 @@ func (a *ACL) RoleSet(args *structs.ACLRoleSetRequest, reply *structs.ACLRole) e } // Verify the role exists - _, existing, err := state.ACLRoleGetByID(nil, role.ID) + _, existing, err := state.ACLRoleGetByID(nil, role.ID, nil) if err != nil { return fmt.Errorf("acl role lookup failed: %v", err) } else if existing == nil { @@ -1349,7 +1437,7 @@ func (a *ACL) RoleSet(args *structs.ACLRoleSetRequest, reply *structs.ACLRole) e } if existing.Name != role.Name { - if _, nameMatch, err := state.ACLRoleGetByName(nil, role.Name); err != nil { + if _, nameMatch, err := state.ACLRoleGetByName(nil, role.Name, &role.EnterpriseMeta); err != nil { return fmt.Errorf("acl role lookup by name failed: %v", err) } else if nameMatch != nil { return fmt.Errorf("Invalid Role: A role with name %q already exists", role.Name) @@ -1363,7 +1451,7 @@ func (a *ACL) RoleSet(args *structs.ACLRoleSetRequest, reply *structs.ACLRole) e // Validate all the policy names and convert them to policy IDs for _, link := range role.Policies { if link.ID == "" { - _, policy, err := state.ACLPolicyGetByName(nil, link.Name) + _, policy, err := state.ACLPolicyGetByName(nil, link.Name, &role.EnterpriseMeta) if err != nil { return fmt.Errorf("Error looking up policy for name %q: %v", link.Name, err) } @@ -1413,7 +1501,7 @@ func (a *ACL) RoleSet(args *structs.ACLRoleSetRequest, reply *structs.ACLRole) e return respErr } - if _, role, err := a.srv.fsm.State().ACLRoleGetByID(nil, role.ID); err == nil && role != nil { + if _, role, err := a.srv.fsm.State().ACLRoleGetByID(nil, role.ID, &role.EnterpriseMeta); err == nil && role != nil { *reply = *role } @@ -1436,14 +1524,16 @@ func (a *ACL) RoleDelete(args *structs.ACLRoleDeleteRequest, reply *string) erro defer metrics.MeasureSince([]string{"acl", "role", "delete"}, time.Now()) // Verify token is permitted to modify ACLs - // TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it + var entCtx acl.EnterpriseAuthorizerContext + args.FillAuthzContext(&entCtx) + if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLWrite(nil) != acl.Allow { + } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } - _, role, err := a.srv.fsm.State().ACLRoleGetByID(nil, args.RoleID) + _, role, err := a.srv.fsm.State().ACLRoleGetByID(nil, args.RoleID, &args.EnterpriseMeta) if err != nil { return err } @@ -1483,20 +1573,22 @@ func (a *ACL) RoleList(args *structs.ACLRoleListRequest, reply *structs.ACLRoleL return err } - // TODO (namespaces) update to call ACLRead with an authz context once ACLs support it - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + rule, err := a.srv.ResolveToken(args.Token) + if err != nil { return err - } else if rule == nil || rule.ACLRead(nil) != acl.Allow { + } else if rule == nil { return acl.ErrPermissionDenied } return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta, func(ws memdb.WatchSet, state *state.Store) error { - index, roles, err := state.ACLRoleList(ws, args.Policy) + index, roles, err := state.ACLRoleList(ws, args.Policy, &args.EnterpriseMeta) if err != nil { return err } + a.srv.filterACLWithAuthorizer(rule, &roles) + reply.Index, reply.Roles = index, roles return nil }) @@ -1560,16 +1652,19 @@ func (a *ACL) BindingRuleRead(args *structs.ACLBindingRuleGetRequest, reply *str return err } - // TODO (namespaces) update to call ACLRead with an authz context once ACLs support it - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + var entCtx acl.EnterpriseAuthorizerContext + args.FillAuthzContext(&entCtx) + + rule, err := a.srv.ResolveToken(args.Token) + if err != nil { return err - } else if rule == nil || rule.ACLRead(nil) != acl.Allow { + } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta, func(ws memdb.WatchSet, state *state.Store) error { - index, rule, err := state.ACLBindingRuleGetByID(ws, args.BindingRuleID) + index, rule, err := state.ACLBindingRuleGetByID(ws, args.BindingRuleID, &args.EnterpriseMeta) if err != nil { return err @@ -1595,14 +1690,17 @@ func (a *ACL) BindingRuleSet(args *structs.ACLBindingRuleSetRequest, reply *stru defer metrics.MeasureSince([]string{"acl", "bindingrule", "upsert"}, time.Now()) + var entCtx acl.EnterpriseAuthorizerContext + args.BindingRule.FillAuthzContext(&entCtx) + // Verify token is permitted to modify ACLs - // TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLWrite(nil) != acl.Allow { + } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } + var existing *structs.ACLBindingRule rule := &args.BindingRule state := a.srv.fsm.State() @@ -1620,7 +1718,8 @@ func (a *ACL) BindingRuleSet(args *structs.ACLBindingRuleSetRequest, reply *stru } // Verify the role exists - _, existing, err := state.ACLBindingRuleGetByID(nil, rule.ID) + var err error + _, existing, err = state.ACLBindingRuleGetByID(nil, rule.ID, nil) if err != nil { return fmt.Errorf("acl binding rule lookup failed: %v", err) } else if existing == nil { @@ -1638,7 +1737,12 @@ func (a *ACL) BindingRuleSet(args *structs.ACLBindingRuleSetRequest, reply *stru return fmt.Errorf("Invalid Binding Rule: no AuthMethod is set") } - methodIdx, method, err := state.ACLAuthMethodGetByName(nil, rule.AuthMethod) + // this is done early here to produce better errors + if err := state.ACLBindingRuleUpsertValidateEnterprise(rule, existing); err != nil { + return err + } + + methodIdx, method, err := state.ACLAuthMethodGetByName(nil, rule.AuthMethod, &args.BindingRule.EnterpriseMeta) if err != nil { return fmt.Errorf("acl auth method lookup failed: %v", err) } else if method == nil { @@ -1691,7 +1795,7 @@ func (a *ACL) BindingRuleSet(args *structs.ACLBindingRuleSetRequest, reply *stru return respErr } - if _, rule, err := a.srv.fsm.State().ACLBindingRuleGetByID(nil, rule.ID); err == nil && rule != nil { + if _, rule, err := a.srv.fsm.State().ACLBindingRuleGetByID(nil, rule.ID, &rule.EnterpriseMeta); err == nil && rule != nil { *reply = *rule } @@ -1713,15 +1817,17 @@ func (a *ACL) BindingRuleDelete(args *structs.ACLBindingRuleDeleteRequest, reply defer metrics.MeasureSince([]string{"acl", "bindingrule", "delete"}, time.Now()) + var entCtx acl.EnterpriseAuthorizerContext + args.FillAuthzContext(&entCtx) + // Verify token is permitted to modify ACLs - // TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLWrite(nil) != acl.Allow { + } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } - _, rule, err := a.srv.fsm.State().ACLBindingRuleGetByID(nil, args.BindingRuleID) + _, rule, err := a.srv.fsm.State().ACLBindingRuleGetByID(nil, args.BindingRuleID, &args.EnterpriseMeta) if err != nil { return err } @@ -1761,20 +1867,22 @@ func (a *ACL) BindingRuleList(args *structs.ACLBindingRuleListRequest, reply *st return err } - // TODO (namespaces) update to call ACLRead with an authz context once ACLs support it - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + rule, err := a.srv.ResolveToken(args.Token) + if err != nil { return err - } else if rule == nil || rule.ACLRead(nil) != acl.Allow { + } else if rule == nil { return acl.ErrPermissionDenied } return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta, func(ws memdb.WatchSet, state *state.Store) error { - index, rules, err := state.ACLBindingRuleList(ws, args.AuthMethod) + index, rules, err := state.ACLBindingRuleList(ws, args.AuthMethod, &args.EnterpriseMeta) if err != nil { return err } + a.srv.filterACLWithAuthorizer(rule, &rules) + reply.Index, reply.BindingRules = index, rules return nil }) @@ -1793,16 +1901,18 @@ func (a *ACL) AuthMethodRead(args *structs.ACLAuthMethodGetRequest, reply *struc return err } - // TODO (namespaces) update to call ACLRead with an authz context once ACLs support it + var entCtx acl.EnterpriseAuthorizerContext + args.FillAuthzContext(&entCtx) + if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLRead(nil) != acl.Allow { + } else if rule == nil || rule.ACLRead(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta, func(ws memdb.WatchSet, state *state.Store) error { - index, method, err := state.ACLAuthMethodGetByName(ws, args.AuthMethodName) + index, method, err := state.ACLAuthMethodGetByName(ws, args.AuthMethodName, &args.EnterpriseMeta) if err != nil { return err @@ -1829,10 +1939,12 @@ func (a *ACL) AuthMethodSet(args *structs.ACLAuthMethodSetRequest, reply *struct defer metrics.MeasureSince([]string{"acl", "authmethod", "upsert"}, time.Now()) // Verify token is permitted to modify ACLs - // TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it + var entCtx acl.EnterpriseAuthorizerContext + args.AuthMethod.FillAuthzContext(&entCtx) + if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLWrite(nil) != acl.Allow { + } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } @@ -1848,7 +1960,7 @@ func (a *ACL) AuthMethodSet(args *structs.ACLAuthMethodSetRequest, reply *struct } // Check to see if the method exists first. - _, existing, err := state.ACLAuthMethodGetByName(nil, method.Name) + _, existing, err := state.ACLAuthMethodGetByName(nil, method.Name, &method.EnterpriseMeta) if err != nil { return fmt.Errorf("acl auth method lookup failed: %v", err) } @@ -1871,6 +1983,10 @@ func (a *ACL) AuthMethodSet(args *structs.ACLAuthMethodSetRequest, reply *struct return fmt.Errorf("Invalid Auth Method: %v", err) } + if err := a.srv.fsm.State().ACLAuthMethodUpsertValidateEnterprise(method, existing); err != nil { + return err + } + req := &structs.ACLAuthMethodBatchSetRequest{ AuthMethods: structs.ACLAuthMethods{method}, } @@ -1884,7 +2000,7 @@ func (a *ACL) AuthMethodSet(args *structs.ACLAuthMethodSetRequest, reply *struct return respErr } - if _, method, err := a.srv.fsm.State().ACLAuthMethodGetByName(nil, method.Name); err == nil && method != nil { + if _, method, err := a.srv.fsm.State().ACLAuthMethodGetByName(nil, method.Name, &method.EnterpriseMeta); err == nil && method != nil { *reply = *method } @@ -1907,14 +2023,16 @@ func (a *ACL) AuthMethodDelete(args *structs.ACLAuthMethodDeleteRequest, reply * defer metrics.MeasureSince([]string{"acl", "authmethod", "delete"}, time.Now()) // Verify token is permitted to modify ACLs - // TODO (namespaces) update to call ACLWrite with an authz context once ACLs support it + var entCtx acl.EnterpriseAuthorizerContext + args.FillAuthzContext(&entCtx) + if rule, err := a.srv.ResolveToken(args.Token); err != nil { return err - } else if rule == nil || rule.ACLWrite(nil) != acl.Allow { + } else if rule == nil || rule.ACLWrite(&entCtx) != acl.Allow { return acl.ErrPermissionDenied } - _, method, err := a.srv.fsm.State().ACLAuthMethodGetByName(nil, args.AuthMethodName) + _, method, err := a.srv.fsm.State().ACLAuthMethodGetByName(nil, args.AuthMethodName, &args.EnterpriseMeta) if err != nil { return err } @@ -1925,6 +2043,7 @@ func (a *ACL) AuthMethodDelete(args *structs.ACLAuthMethodDeleteRequest, reply * req := structs.ACLAuthMethodBatchDeleteRequest{ AuthMethodNames: []string{args.AuthMethodName}, + EnterpriseMeta: args.EnterpriseMeta, } resp, err := a.srv.raftApply(structs.ACLAuthMethodDeleteRequestType, &req) @@ -1954,20 +2073,22 @@ func (a *ACL) AuthMethodList(args *structs.ACLAuthMethodListRequest, reply *stru return err } - // TODO (namespaces) update to call ACLRead with an authz context once ACLs support it - if rule, err := a.srv.ResolveToken(args.Token); err != nil { + rule, err := a.srv.ResolveToken(args.Token) + if err != nil { return err - } else if rule == nil || rule.ACLRead(nil) != acl.Allow { + } else if rule == nil { return acl.ErrPermissionDenied } return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta, func(ws memdb.WatchSet, state *state.Store) error { - index, methods, err := state.ACLAuthMethodList(ws) + index, methods, err := state.ACLAuthMethodList(ws, &args.EnterpriseMeta) if err != nil { return err } + a.srv.filterACLWithAuthorizer(rule, &methods) + var stubs structs.ACLAuthMethodListStubs for _, method := range methods { stubs = append(stubs, method.Stub()) @@ -2000,7 +2121,7 @@ func (a *ACL) Login(args *structs.ACLLoginRequest, reply *structs.ACLToken) erro auth := args.Auth // 1. take args.Data.AuthMethod to get an AuthMethod Validator - idx, method, err := a.srv.fsm.State().ACLAuthMethodGetByName(nil, auth.AuthMethod) + idx, method, err := a.srv.fsm.State().ACLAuthMethodGetByName(nil, auth.AuthMethod, &auth.EnterpriseMeta) if err != nil { return err } else if method == nil { @@ -2019,7 +2140,7 @@ func (a *ACL) Login(args *structs.ACLLoginRequest, reply *structs.ACLToken) erro } // 3. send map through role bindings - serviceIdentities, roleLinks, err := a.srv.evaluateRoleBindings(validator, verifiedFields) + serviceIdentities, roleLinks, err := a.srv.evaluateRoleBindings(validator, verifiedFields, &auth.EnterpriseMeta) if err != nil { return err } @@ -2048,6 +2169,7 @@ func (a *ACL) Login(args *structs.ACLLoginRequest, reply *structs.ACLToken) erro AuthMethod: auth.AuthMethod, ServiceIdentities: serviceIdentities, Roles: roleLinks, + EnterpriseMeta: auth.EnterpriseMeta, }, WriteRequest: args.WriteRequest, } @@ -2097,7 +2219,7 @@ func (a *ACL) Logout(args *structs.ACLLogoutRequest, reply *bool) error { defer metrics.MeasureSince([]string{"acl", "logout"}, time.Now()) - _, token, err := a.srv.fsm.State().ACLTokenGetBySecret(nil, args.Token) + _, token, err := a.srv.fsm.State().ACLTokenGetBySecret(nil, args.Token, nil) if err != nil { return err diff --git a/agent/consul/acl_endpoint_legacy.go b/agent/consul/acl_endpoint_legacy.go index 7b18b3d5a0..e5bad09665 100644 --- a/agent/consul/acl_endpoint_legacy.go +++ b/agent/consul/acl_endpoint_legacy.go @@ -94,7 +94,7 @@ func aclApplyInternal(srv *Server, args *structs.ACLRequest, reply *string) erro } // No need to check expiration times as those did not exist in legacy tokens. - _, existing, _ := srv.fsm.State().ACLTokenGetBySecret(nil, args.ACL.ID) + _, existing, _ := srv.fsm.State().ACLTokenGetBySecret(nil, args.ACL.ID, nil) if existing != nil && existing.UsesNonLegacyFields() { return fmt.Errorf("Cannot use legacy endpoint to modify a non-legacy token") } @@ -114,7 +114,7 @@ func aclApplyInternal(srv *Server, args *structs.ACLRequest, reply *string) erro } // Validate the rules compile - _, err := acl.NewPolicyFromSource("", 0, args.ACL.Rules, acl.SyntaxLegacy, srv.enterpriseACLConfig) + _, err := acl.NewPolicyFromSource("", 0, args.ACL.Rules, acl.SyntaxLegacy, srv.enterpriseACLConfig, nil) if err != nil { return fmt.Errorf("ACL rule compilation failed: %v", err) } @@ -211,7 +211,7 @@ func (a *ACL) Get(args *structs.ACLSpecificRequest, return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta, func(ws memdb.WatchSet, state *state.Store) error { - index, token, err := state.ACLTokenGetBySecret(ws, args.ACL) + index, token, err := state.ACLTokenGetBySecret(ws, args.ACL, nil) if err != nil { return err } @@ -262,7 +262,7 @@ func (a *ACL) List(args *structs.DCSpecificRequest, return a.srv.blockingQuery(&args.QueryOptions, &reply.QueryMeta, func(ws memdb.WatchSet, state *state.Store) error { - index, tokens, err := state.ACLTokenList(ws, false, true, "", "", "") + index, tokens, err := state.ACLTokenList(ws, false, true, "", "", "", nil) if err != nil { return err } diff --git a/agent/consul/acl_endpoint_test.go b/agent/consul/acl_endpoint_test.go index 997810713f..98eb8d8080 100644 --- a/agent/consul/acl_endpoint_test.go +++ b/agent/consul/acl_endpoint_test.go @@ -144,7 +144,7 @@ func TestACLEndpoint_Apply(t *testing.T) { // Verify state := s1.fsm.State() - _, s, err := state.ACLTokenGetBySecret(nil, out) + _, s, err := state.ACLTokenGetBySecret(nil, out, nil) if err != nil { t.Fatalf("err: %v", err) } @@ -166,7 +166,7 @@ func TestACLEndpoint_Apply(t *testing.T) { } // Verify - _, s, err = state.ACLTokenGetBySecret(nil, id) + _, s, err = state.ACLTokenGetBySecret(nil, id, nil) if err != nil { t.Fatalf("err: %v", err) } @@ -289,7 +289,7 @@ func TestACLEndpoint_Apply_CustomID(t *testing.T) { // Verify state := s1.fsm.State() - _, s, err := state.ACLTokenGetBySecret(nil, out) + _, s, err := state.ACLTokenGetBySecret(nil, out, nil) if err != nil { t.Fatalf("err: %v", err) } @@ -733,7 +733,7 @@ func TestACLEndpoint_TokenRead(t *testing.T) { err := acl.TokenRead(&req, &resp) require.Nil(t, resp.Token) - require.EqualError(t, err, "failed acl token lookup: failed acl token lookup: index error: UUID must be 36 characters") + require.EqualError(t, err, "failed acl token lookup: index error: UUID must be 36 characters") }) } @@ -5358,12 +5358,7 @@ func deleteTestPolicy(codec rpc.ClientCodec, masterToken string, datacenter stri return err } -// upsertTestPolicy creates a policy for testing purposes -func upsertTestPolicy(codec rpc.ClientCodec, masterToken string, datacenter string) (*structs.ACLPolicy, error) { - return upsertTestPolicyWithRules(codec, masterToken, datacenter, "") -} - -func upsertTestPolicyWithRules(codec rpc.ClientCodec, masterToken string, datacenter string, rules string) (*structs.ACLPolicy, error) { +func upsertTestCustomizedPolicy(codec rpc.ClientCodec, masterToken string, datacenter string, policyModificationFn func(policy *structs.ACLPolicy)) (*structs.ACLPolicy, error) { // Make sure test policies can't collide policyUnq, err := uuid.GenerateUUID() if err != nil { @@ -5373,12 +5368,15 @@ func upsertTestPolicyWithRules(codec rpc.ClientCodec, masterToken string, datace arg := structs.ACLPolicySetRequest{ Datacenter: datacenter, Policy: structs.ACLPolicy{ - Name: fmt.Sprintf("test-policy-%s", policyUnq), - Rules: rules, + Name: fmt.Sprintf("test-policy-%s", policyUnq), }, WriteRequest: structs.WriteRequest{Token: masterToken}, } + if policyModificationFn != nil { + policyModificationFn(&arg.Policy) + } + var out structs.ACLPolicy err = msgpackrpc.CallWithCodec(codec, "ACL.PolicySet", &arg, &out) @@ -5394,6 +5392,17 @@ func upsertTestPolicyWithRules(codec rpc.ClientCodec, masterToken string, datace return &out, nil } +// upsertTestPolicy creates a policy for testing purposes +func upsertTestPolicy(codec rpc.ClientCodec, masterToken string, datacenter string) (*structs.ACLPolicy, error) { + return upsertTestPolicyWithRules(codec, masterToken, datacenter, "") +} + +func upsertTestPolicyWithRules(codec rpc.ClientCodec, masterToken string, datacenter string, rules string) (*structs.ACLPolicy, error) { + return upsertTestCustomizedPolicy(codec, masterToken, datacenter, func(policy *structs.ACLPolicy) { + policy.Rules = rules + }) +} + // retrieveTestPolicy returns a policy for testing purposes func retrieveTestPolicy(codec rpc.ClientCodec, masterToken string, datacenter string, id string) (*structs.ACLPolicyResponse, error) { arg := structs.ACLPolicyGetRequest{ @@ -5439,6 +5448,10 @@ func deleteTestRoleByName(codec rpc.ClientCodec, masterToken string, datacenter // upsertTestRole creates a role for testing purposes func upsertTestRole(codec rpc.ClientCodec, masterToken string, datacenter string) (*structs.ACLRole, error) { + return upsertTestCustomizedRole(codec, masterToken, datacenter, nil) +} + +func upsertTestCustomizedRole(codec rpc.ClientCodec, masterToken string, datacenter string, modify func(role *structs.ACLRole)) (*structs.ACLRole, error) { // Make sure test roles can't collide roleUnq, err := uuid.GenerateUUID() if err != nil { @@ -5453,6 +5466,10 @@ func upsertTestRole(codec rpc.ClientCodec, masterToken string, datacenter string WriteRequest: structs.WriteRequest{Token: masterToken}, } + if modify != nil { + modify(&arg.Role) + } + var out structs.ACLRole err = msgpackrpc.CallWithCodec(codec, "ACL.RoleSet", &arg, &out) @@ -5518,6 +5535,17 @@ func deleteTestAuthMethod(codec rpc.ClientCodec, masterToken string, datacenter func upsertTestAuthMethod( codec rpc.ClientCodec, masterToken string, datacenter string, sessionID string, +) (*structs.ACLAuthMethod, error) { + return upsertTestCustomizedAuthMethod(codec, masterToken, datacenter, func(method *structs.ACLAuthMethod) { + method.Config = map[string]interface{}{ + "SessionID": sessionID, + } + }) +} + +func upsertTestCustomizedAuthMethod( + codec rpc.ClientCodec, masterToken string, datacenter string, + modify func(method *structs.ACLAuthMethod), ) (*structs.ACLAuthMethod, error) { name, err := uuid.GenerateUUID() if err != nil { @@ -5529,13 +5557,14 @@ func upsertTestAuthMethod( AuthMethod: structs.ACLAuthMethod{ Name: "test-method-" + name, Type: "testing", - Config: map[string]interface{}{ - "SessionID": sessionID, - }, }, WriteRequest: structs.WriteRequest{Token: masterToken}, } + if modify != nil { + modify(&req.AuthMethod) + } + var out structs.ACLAuthMethod err = msgpackrpc.CallWithCodec(codec, "ACL.AuthMethodSet", &req, &out) @@ -5625,17 +5654,25 @@ func upsertTestBindingRule( bindType string, bindName string, ) (*structs.ACLBindingRule, error) { + return upsertTestCustomizedBindingRule(codec, masterToken, datacenter, func(rule *structs.ACLBindingRule) { + rule.AuthMethod = methodName + rule.BindType = bindType + rule.BindName = bindName + rule.Selector = selector + }) +} + +func upsertTestCustomizedBindingRule(codec rpc.ClientCodec, masterToken string, datacenter string, modify func(rule *structs.ACLBindingRule)) (*structs.ACLBindingRule, error) { req := structs.ACLBindingRuleSetRequest{ - Datacenter: datacenter, - BindingRule: structs.ACLBindingRule{ - AuthMethod: methodName, - BindType: bindType, - BindName: bindName, - Selector: selector, - }, + Datacenter: datacenter, + BindingRule: structs.ACLBindingRule{}, WriteRequest: structs.WriteRequest{Token: masterToken}, } + if modify != nil { + modify(&req.BindingRule) + } + var out structs.ACLBindingRule err := msgpackrpc.CallWithCodec(codec, "ACL.BindingRuleSet", &req, &out) diff --git a/agent/consul/acl_replication_legacy.go b/agent/consul/acl_replication_legacy.go index b933f714e9..a14e416f20 100644 --- a/agent/consul/acl_replication_legacy.go +++ b/agent/consul/acl_replication_legacy.go @@ -138,7 +138,7 @@ func reconcileLegacyACLs(local, remote structs.ACLs, lastRemoteIndex uint64) str // FetchLocalACLs returns the ACLs in the local state store. func (s *Server) fetchLocalLegacyACLs() (structs.ACLs, error) { - _, local, err := s.fsm.State().ACLTokenList(nil, false, true, "", "", "") + _, local, err := s.fsm.State().ACLTokenList(nil, false, true, "", "", "", nil) if err != nil { return nil, err } diff --git a/agent/consul/acl_replication_legacy_test.go b/agent/consul/acl_replication_legacy_test.go index 1424e5f239..9149122aeb 100644 --- a/agent/consul/acl_replication_legacy_test.go +++ b/agent/consul/acl_replication_legacy_test.go @@ -396,11 +396,11 @@ func TestACLReplication_LegacyTokens(t *testing.T) { } checkSame := func() error { - index, remote, err := s1.fsm.State().ACLTokenList(nil, true, true, "", "", "") + index, remote, err := s1.fsm.State().ACLTokenList(nil, true, true, "", "", "", nil) if err != nil { return err } - _, local, err := s2.fsm.State().ACLTokenList(nil, true, true, "", "", "") + _, local, err := s2.fsm.State().ACLTokenList(nil, true, true, "", "", "", nil) if err != nil { return err } diff --git a/agent/consul/acl_replication_test.go b/agent/consul/acl_replication_test.go index 89ef37b7dd..f2b8ed91c0 100644 --- a/agent/consul/acl_replication_test.go +++ b/agent/consul/acl_replication_test.go @@ -351,9 +351,9 @@ func TestACLReplication_Tokens(t *testing.T) { checkSame := func(t *retry.R) { // only account for global tokens - local tokens shouldn't be replicated - index, remote, err := s1.fsm.State().ACLTokenList(nil, false, true, "", "", "") + index, remote, err := s1.fsm.State().ACLTokenList(nil, false, true, "", "", "", nil) require.NoError(t, err) - _, local, err := s2.fsm.State().ACLTokenList(nil, false, true, "", "", "") + _, local, err := s2.fsm.State().ACLTokenList(nil, false, true, "", "", "", nil) require.NoError(t, err) require.Len(t, local, len(remote)) @@ -378,7 +378,7 @@ func TestACLReplication_Tokens(t *testing.T) { // Wait for s2 global-management policy retry.Run(t, func(r *retry.R) { - _, policy, err := s2.fsm.State().ACLPolicyGetByID(nil, structs.ACLPolicyGlobalManagementID) + _, policy, err := s2.fsm.State().ACLPolicyGetByID(nil, structs.ACLPolicyGlobalManagementID, nil) require.NoError(r, err) require.NotNil(r, policy) }) @@ -451,7 +451,7 @@ func TestACLReplication_Tokens(t *testing.T) { }) // verify dc2 local tokens didn't get blown away - _, local, err := s2.fsm.State().ACLTokenList(nil, true, false, "", "", "") + _, local, err := s2.fsm.State().ACLTokenList(nil, true, false, "", "", "", nil) require.NoError(t, err) require.Len(t, local, 50) @@ -529,9 +529,9 @@ func TestACLReplication_Policies(t *testing.T) { checkSame := func(t *retry.R) { // only account for global tokens - local tokens shouldn't be replicated - index, remote, err := s1.fsm.State().ACLPolicyList(nil) + index, remote, err := s1.fsm.State().ACLPolicyList(nil, nil) require.NoError(t, err) - _, local, err := s2.fsm.State().ACLPolicyList(nil) + _, local, err := s2.fsm.State().ACLPolicyList(nil, nil) require.NoError(t, err) require.Len(t, local, len(remote)) @@ -787,10 +787,10 @@ func TestACLReplication_AllTypes(t *testing.T) { checkSameTokens := func(t *retry.R) { // only account for global tokens - local tokens shouldn't be replicated - index, remote, err := s1.fsm.State().ACLTokenList(nil, false, true, "", "", "") + index, remote, err := s1.fsm.State().ACLTokenList(nil, false, true, "", "", "", nil) require.NoError(t, err) // Query for all of them, so that we can prove that no globals snuck in. - _, local, err := s2.fsm.State().ACLTokenList(nil, true, true, "", "", "") + _, local, err := s2.fsm.State().ACLTokenList(nil, true, true, "", "", "", nil) require.NoError(t, err) require.Len(t, remote, len(local)) @@ -809,9 +809,9 @@ func TestACLReplication_AllTypes(t *testing.T) { require.Equal(t, status.SourceDatacenter, "dc1") } checkSamePolicies := func(t *retry.R) { - index, remote, err := s1.fsm.State().ACLPolicyList(nil) + index, remote, err := s1.fsm.State().ACLPolicyList(nil, nil) require.NoError(t, err) - _, local, err := s2.fsm.State().ACLPolicyList(nil) + _, local, err := s2.fsm.State().ACLPolicyList(nil, nil) require.NoError(t, err) require.Len(t, remote, len(local)) @@ -830,9 +830,9 @@ func TestACLReplication_AllTypes(t *testing.T) { require.Equal(t, status.SourceDatacenter, "dc1") } checkSameRoles := func(t *retry.R) { - index, remote, err := s1.fsm.State().ACLRoleList(nil, "") + index, remote, err := s1.fsm.State().ACLRoleList(nil, "", nil) require.NoError(t, err) - _, local, err := s2.fsm.State().ACLRoleList(nil, "") + _, local, err := s2.fsm.State().ACLRoleList(nil, "", nil) require.NoError(t, err) require.Len(t, remote, len(local)) diff --git a/agent/consul/acl_replication_types.go b/agent/consul/acl_replication_types.go index e0222e1443..a7a703dac2 100644 --- a/agent/consul/acl_replication_types.go +++ b/agent/consul/acl_replication_types.go @@ -34,7 +34,7 @@ func (r *aclTokenReplicator) FetchRemote(srv *Server, lastRemoteIndex uint64) (i func (r *aclTokenReplicator) FetchLocal(srv *Server) (int, uint64, error) { r.local = nil - idx, local, err := srv.fsm.State().ACLTokenList(nil, false, true, "", "", "") + idx, local, err := srv.fsm.State().ACLTokenList(nil, false, true, "", "", "", structs.ReplicationEnterpriseMeta()) if err != nil { return 0, 0, err } @@ -155,7 +155,7 @@ func (r *aclPolicyReplicator) FetchRemote(srv *Server, lastRemoteIndex uint64) ( func (r *aclPolicyReplicator) FetchLocal(srv *Server) (int, uint64, error) { r.local = nil - idx, local, err := srv.fsm.State().ACLPolicyList(nil) + idx, local, err := srv.fsm.State().ACLPolicyList(nil, structs.ReplicationEnterpriseMeta()) if err != nil { return 0, 0, err } @@ -265,7 +265,7 @@ func (r *aclRoleReplicator) FetchRemote(srv *Server, lastRemoteIndex uint64) (in func (r *aclRoleReplicator) FetchLocal(srv *Server) (int, uint64, error) { r.local = nil - idx, local, err := srv.fsm.State().ACLRoleList(nil, "") + idx, local, err := srv.fsm.State().ACLRoleList(nil, "", nil) if err != nil { return 0, 0, err } diff --git a/agent/consul/acl_server.go b/agent/consul/acl_server.go index 34ca09584b..463d82a32a 100644 --- a/agent/consul/acl_server.go +++ b/agent/consul/acl_server.go @@ -36,13 +36,13 @@ func (s *Server) checkTokenUUID(id string) (bool, error) { // a token that hasn't been reaped yet, then we won't be able to insert the // new token due to a collision. - if _, token, err := state.ACLTokenGetByAccessor(nil, id); err != nil { + if _, token, err := state.ACLTokenGetByAccessor(nil, id, nil); err != nil { return false, err } else if token != nil { return false, nil } - if _, token, err := state.ACLTokenGetBySecret(nil, id); err != nil { + if _, token, err := state.ACLTokenGetBySecret(nil, id, nil); err != nil { return false, err } else if token != nil { return false, nil @@ -53,7 +53,7 @@ func (s *Server) checkTokenUUID(id string) (bool, error) { func (s *Server) checkPolicyUUID(id string) (bool, error) { state := s.fsm.State() - if _, policy, err := state.ACLPolicyGetByID(nil, id); err != nil { + if _, policy, err := state.ACLPolicyGetByID(nil, id, nil); err != nil { return false, err } else if policy != nil { return false, nil @@ -64,7 +64,7 @@ func (s *Server) checkPolicyUUID(id string) (bool, error) { func (s *Server) checkRoleUUID(id string) (bool, error) { state := s.fsm.State() - if _, role, err := state.ACLRoleGetByID(nil, id); err != nil { + if _, role, err := state.ACLRoleGetByID(nil, id, nil); err != nil { return false, err } else if role != nil { return false, nil @@ -75,7 +75,7 @@ func (s *Server) checkRoleUUID(id string) (bool, error) { func (s *Server) checkBindingRuleUUID(id string) (bool, error) { state := s.fsm.State() - if _, rule, err := state.ACLBindingRuleGetByID(nil, id); err != nil { + if _, rule, err := state.ACLBindingRuleGetByID(nil, id, nil); err != nil { return false, err } else if rule != nil { return false, nil @@ -171,7 +171,7 @@ func (s *Server) ResolveIdentityFromToken(token string) (bool, structs.ACLIdenti return false, nil, nil } - index, aclToken, err := s.fsm.State().ACLTokenGetBySecret(nil, token) + index, aclToken, err := s.fsm.State().ACLTokenGetBySecret(nil, token, nil) if err != nil { return true, nil, err } else if aclToken != nil && !aclToken.IsExpired(time.Now()) { @@ -182,7 +182,7 @@ func (s *Server) ResolveIdentityFromToken(token string) (bool, structs.ACLIdenti } func (s *Server) ResolvePolicyFromID(policyID string) (bool, *structs.ACLPolicy, error) { - index, policy, err := s.fsm.State().ACLPolicyGetByID(nil, policyID) + index, policy, err := s.fsm.State().ACLPolicyGetByID(nil, policyID, nil) if err != nil { return true, nil, err } else if policy != nil { @@ -196,7 +196,7 @@ func (s *Server) ResolvePolicyFromID(policyID string) (bool, *structs.ACLPolicy, } func (s *Server) ResolveRoleFromID(roleID string) (bool, *structs.ACLRole, error) { - index, role, err := s.fsm.State().ACLRoleGetByID(nil, roleID) + index, role, err := s.fsm.State().ACLRoleGetByID(nil, roleID, nil) if err != nil { return true, nil, err } else if role != nil { diff --git a/agent/consul/acl_test.go b/agent/consul/acl_test.go index 246b40b7eb..1de2ec4d81 100644 --- a/agent/consul/acl_test.go +++ b/agent/consul/acl_test.go @@ -2176,7 +2176,7 @@ func TestACL_filterHealthChecks(t *testing.T) { service "foo" { policy = "read" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -2210,7 +2210,7 @@ service "foo" { node "node1" { policy = "read" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -2268,7 +2268,7 @@ func TestACL_filterIntentions(t *testing.T) { service "foo" { policy = "read" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) assert.Nil(err) perms, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil) assert.Nil(err) @@ -2353,7 +2353,7 @@ func TestACL_filterServiceNodes(t *testing.T) { service "foo" { policy = "read" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -2387,7 +2387,7 @@ service "foo" { node "node1" { policy = "read" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -2459,7 +2459,7 @@ func TestACL_filterNodeServices(t *testing.T) { service "foo" { policy = "read" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -2493,7 +2493,7 @@ service "foo" { node "node1" { policy = "read" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -2565,7 +2565,7 @@ func TestACL_filterCheckServiceNodes(t *testing.T) { service "foo" { policy = "read" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -2602,7 +2602,7 @@ service "foo" { node "node1" { policy = "read" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -2756,7 +2756,7 @@ func TestACL_filterNodeDump(t *testing.T) { service "foo" { policy = "read" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -2796,7 +2796,7 @@ service "foo" { node "node1" { policy = "read" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -3052,7 +3052,7 @@ func TestACL_vetRegisterWithACL(t *testing.T) { node "node" { policy = "write" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -3097,7 +3097,7 @@ node "node" { service "service" { policy = "write" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -3127,7 +3127,7 @@ service "service" { service "other" { policy = "write" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -3201,7 +3201,7 @@ service "other" { service "other" { policy = "deny" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -3231,7 +3231,7 @@ service "other" { node "node" { policy = "deny" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -3278,7 +3278,7 @@ func TestACL_vetDeregisterWithACL(t *testing.T) { node "node" { policy = "write" } -`, acl.SyntaxLegacy, nil) +`, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } @@ -3291,7 +3291,7 @@ node "node" { service "my-service" { policy = "write" } - `, acl.SyntaxLegacy, nil) + `, acl.SyntaxLegacy, nil, nil) if err != nil { t.Fatalf("err %v", err) } diff --git a/agent/consul/authmethod/authmethods.go b/agent/consul/authmethod/authmethods.go index 8fd477d0f2..65a4e07faa 100644 --- a/agent/consul/authmethod/authmethods.go +++ b/agent/consul/authmethod/authmethods.go @@ -9,6 +9,21 @@ import ( "github.com/mitchellh/mapstructure" ) +type Cache interface { + // GetValidator retrieves the Validator from the cache. + // It returns the modify index of struct that the validator was created from, + // the validator and a boolean indicating whether the value was found + GetValidator(method *structs.ACLAuthMethod) (uint64, Validator, bool) + + // PutValidatorIfNewer inserts a new validator into the cache if the index is greater + // than the modify index of any existing entry in the cache. This method will return + // the newest validator which may or may not be the one from the method parameter + PutValidatorIfNewer(method *structs.ACLAuthMethod, validator Validator, idx uint64) Validator + + // Purge removes all cached validators + Purge() +} + type ValidatorFactory func(method *structs.ACLAuthMethod) (Validator, error) type Validator interface { @@ -64,6 +79,54 @@ func IsRegisteredType(typeName string) bool { return ok } +type authMethodValidatorEntry struct { + Validator Validator + ModifyIndex uint64 +} + +// authMethodCache is an non-thread-safe cache that maps ACLAuthMethods to their Validators +type authMethodCache struct { + entries map[string]*authMethodValidatorEntry +} + +func newCache() Cache { + c := &authMethodCache{} + c.init() + return c +} + +func (c *authMethodCache) init() { + c.Purge() +} + +func (c *authMethodCache) GetValidator(method *structs.ACLAuthMethod) (uint64, Validator, bool) { + entry, ok := c.entries[method.Name] + if ok { + return entry.ModifyIndex, entry.Validator, true + } + + return 0, nil, false +} + +func (c *authMethodCache) PutValidatorIfNewer(method *structs.ACLAuthMethod, validator Validator, idx uint64) Validator { + prev, ok := c.entries[method.Name] + if ok { + if prev.ModifyIndex >= idx { + return prev.Validator + } + } + + c.entries[method.Name] = &authMethodValidatorEntry{ + Validator: validator, + ModifyIndex: idx, + } + return validator +} + +func (c *authMethodCache) Purge() { + c.entries = make(map[string]*authMethodValidatorEntry) +} + // NewValidator instantiates a new Validator for the given auth method // configuration. If no auth method is registered with the provided type an // error is returned. diff --git a/agent/consul/authmethod/authmethods_oss.go b/agent/consul/authmethod/authmethods_oss.go new file mode 100644 index 0000000000..e9e7f7609f --- /dev/null +++ b/agent/consul/authmethod/authmethods_oss.go @@ -0,0 +1,38 @@ +// +build !consulent + +package authmethod + +import ( + "sync" + + "github.com/hashicorp/consul/agent/structs" +) + +type syncCache struct { + lock sync.RWMutex + cache authMethodCache +} + +func NewCache() Cache { + c := &syncCache{} + c.cache.init() + return c +} + +func (c *syncCache) GetValidator(method *structs.ACLAuthMethod) (uint64, Validator, bool) { + c.lock.RLock() + defer c.lock.RUnlock() + return c.cache.GetValidator(method) +} + +func (c *syncCache) PutValidatorIfNewer(method *structs.ACLAuthMethod, validator Validator, idx uint64) Validator { + c.lock.Lock() + defer c.lock.Unlock() + return c.cache.PutValidatorIfNewer(method, validator, idx) +} + +func (c *syncCache) Purge() { + c.lock.Lock() + c.cache.Purge() + c.lock.Unlock() +} diff --git a/agent/consul/filter_test.go b/agent/consul/filter_test.go index 5cf4a4c71e..a575494e4a 100644 --- a/agent/consul/filter_test.go +++ b/agent/consul/filter_test.go @@ -10,7 +10,7 @@ import ( func TestFilter_DirEnt(t *testing.T) { t.Parallel() - policy, _ := acl.NewPolicyFromSource("", 0, testFilterRules, acl.SyntaxLegacy, nil) + policy, _ := acl.NewPolicyFromSource("", 0, testFilterRules, acl.SyntaxLegacy, nil, nil) aclR, _ := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil) type tcase struct { @@ -52,7 +52,7 @@ func TestFilter_DirEnt(t *testing.T) { func TestFilter_Keys(t *testing.T) { t.Parallel() - policy, _ := acl.NewPolicyFromSource("", 0, testFilterRules, acl.SyntaxLegacy, nil) + policy, _ := acl.NewPolicyFromSource("", 0, testFilterRules, acl.SyntaxLegacy, nil, nil) aclR, _ := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil) type tcase struct { @@ -84,7 +84,7 @@ func TestFilter_Keys(t *testing.T) { func TestFilter_TxnResults(t *testing.T) { t.Parallel() - policy, _ := acl.NewPolicyFromSource("", 0, testFilterRules, acl.SyntaxLegacy, nil) + policy, _ := acl.NewPolicyFromSource("", 0, testFilterRules, acl.SyntaxLegacy, nil, nil) aclR, _ := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil) type tcase struct { diff --git a/agent/consul/fsm/commands_oss.go b/agent/consul/fsm/commands_oss.go index 65d47807db..5e48c1a6b3 100644 --- a/agent/consul/fsm/commands_oss.go +++ b/agent/consul/fsm/commands_oss.go @@ -172,7 +172,7 @@ func (c *FSM) applyACLOperation(buf []byte, index uint64) interface{} { } // No need to check expiration times as those did not exist in legacy tokens. - if _, token, err := c.state.ACLTokenGetBySecret(nil, req.ACL.ID); err != nil { + if _, token, err := c.state.ACLTokenGetBySecret(nil, req.ACL.ID, nil); err != nil { return err } else { acl, err := token.Convert() @@ -188,7 +188,7 @@ func (c *FSM) applyACLOperation(buf []byte, index uint64) interface{} { } return req.ACL.ID case structs.ACLDelete: - return c.state.ACLTokenDeleteBySecret(index, req.ACL.ID) + return c.state.ACLTokenDeleteBySecret(index, req.ACL.ID, nil) default: c.logger.Printf("[WARN] consul.fsm: Invalid ACL operation '%s'", req.Op) return fmt.Errorf("Invalid ACL operation '%s'", req.Op) @@ -533,5 +533,5 @@ func (c *FSM) applyACLAuthMethodDeleteOperation(buf []byte, index uint64) interf defer metrics.MeasureSinceWithLabels([]string{"fsm", "acl", "authmethod"}, time.Now(), []metrics.Label{{Name: "op", Value: "delete"}}) - return c.state.ACLAuthMethodBatchDelete(index, req.AuthMethodNames) + return c.state.ACLAuthMethodBatchDelete(index, req.AuthMethodNames, &req.EnterpriseMeta) } diff --git a/agent/consul/fsm/commands_oss_test.go b/agent/consul/fsm/commands_oss_test.go index d6cba2d9b1..60f5289a05 100644 --- a/agent/consul/fsm/commands_oss_test.go +++ b/agent/consul/fsm/commands_oss_test.go @@ -816,7 +816,7 @@ func TestFSM_ACL_CRUD(t *testing.T) { // Get the ACL. id := resp.(string) - _, acl, err := fsm.state.ACLTokenGetBySecret(nil, id) + _, acl, err := fsm.state.ACLTokenGetBySecret(nil, id, nil) if err != nil { t.Fatalf("err: %v", err) } @@ -852,7 +852,7 @@ func TestFSM_ACL_CRUD(t *testing.T) { t.Fatalf("resp: %v", resp) } - _, acl, err = fsm.state.ACLTokenGetBySecret(nil, id) + _, acl, err = fsm.state.ACLTokenGetBySecret(nil, id, nil) if err != nil { t.Fatalf("err: %v", err) } diff --git a/agent/consul/fsm/snapshot_oss_test.go b/agent/consul/fsm/snapshot_oss_test.go index c22aff816f..39e46a9a73 100644 --- a/agent/consul/fsm/snapshot_oss_test.go +++ b/agent/consul/fsm/snapshot_oss_test.go @@ -371,17 +371,17 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) { } // Verify ACL Binding Rule is restored - _, bindingRule2, err := fsm2.state.ACLBindingRuleGetByID(nil, bindingRule.ID) + _, bindingRule2, err := fsm2.state.ACLBindingRuleGetByID(nil, bindingRule.ID, nil) require.NoError(err) require.Equal(bindingRule, bindingRule2) // Verify ACL Auth Method is restored - _, method2, err := fsm2.state.ACLAuthMethodGetByName(nil, method.Name) + _, method2, err := fsm2.state.ACLAuthMethodGetByName(nil, method.Name, nil) require.NoError(err) require.Equal(method, method2) // Verify ACL Token is restored - _, token2, err := fsm2.state.ACLTokenGetByAccessor(nil, token.AccessorID) + _, token2, err := fsm2.state.ACLTokenGetByAccessor(nil, token.AccessorID, nil) require.NoError(err) { // time.Time is tricky to compare generically when it takes a ser/deserialization round trip. @@ -396,12 +396,12 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) { require.True(index > 0) // Verify ACL Role is restored - _, role2, err := fsm2.state.ACLRoleGetByID(nil, role.ID) + _, role2, err := fsm2.state.ACLRoleGetByID(nil, role.ID, nil) require.NoError(err) require.Equal(role, role2) // Verify ACL Policy is restored - _, policy2, err := fsm2.state.ACLPolicyGetByID(nil, structs.ACLPolicyGlobalManagementID) + _, policy2, err := fsm2.state.ACLPolicyGetByID(nil, structs.ACLPolicyGlobalManagementID, nil) require.NoError(err) require.Equal(policy, policy2) diff --git a/agent/consul/leader.go b/agent/consul/leader.go index f14492c53f..5a30a49474 100644 --- a/agent/consul/leader.go +++ b/agent/consul/leader.go @@ -371,7 +371,7 @@ func (s *Server) initializeLegacyACL() error { // Create anonymous token if missing. state := s.fsm.State() - _, token, err := state.ACLTokenGetBySecret(nil, anonymousToken) + _, token, err := state.ACLTokenGetBySecret(nil, anonymousToken, nil) if err != nil { return fmt.Errorf("failed to get anonymous token: %v", err) } @@ -395,7 +395,7 @@ func (s *Server) initializeLegacyACL() error { // Check for configured master token. if master := s.config.ACLMasterToken; len(master) > 0 { - _, token, err = state.ACLTokenGetBySecret(nil, master) + _, token, err = state.ACLTokenGetBySecret(nil, master, nil) if err != nil { return fmt.Errorf("failed to get master token: %v", err) } @@ -473,11 +473,11 @@ func (s *Server) initializeACLs(upgrade bool) error { // Purge the auth method validators since they could've changed while we // were not leader. - s.purgeAuthMethodValidators() + s.aclAuthMethodValidators.Purge() // Remove any token affected by CVE-2019-8336 if !s.InACLDatacenter() { - _, token, err := s.fsm.State().ACLTokenGetBySecret(nil, redactedToken) + _, token, err := s.fsm.State().ACLTokenGetBySecret(nil, redactedToken, nil) if err == nil && token != nil { req := structs.ACLTokenBatchDeleteRequest{ TokenIDs: []string{token.AccessorID}, @@ -499,7 +499,7 @@ func (s *Server) initializeACLs(upgrade bool) error { s.logger.Printf("[INFO] acl: initializing acls") // Create/Upgrade the builtin global-management policy - _, policy, err := s.fsm.State().ACLPolicyGetByID(nil, structs.ACLPolicyGlobalManagementID) + _, policy, err := s.fsm.State().ACLPolicyGetByID(nil, structs.ACLPolicyGlobalManagementID, structs.DefaultEnterpriseMeta()) if err != nil { return fmt.Errorf("failed to get the builtin global-management policy") } @@ -516,6 +516,7 @@ func (s *Server) initializeACLs(upgrade bool) error { newPolicy.Description = policy.Description } + newPolicy.EnterpriseMeta.InitDefault() newPolicy.SetHash(true) req := structs.ACLPolicyBatchSetRequest{ @@ -535,7 +536,7 @@ func (s *Server) initializeACLs(upgrade bool) error { s.logger.Printf("[WARN] consul: Configuring a non-UUID master token is deprecated") } - _, token, err := state.ACLTokenGetBySecret(nil, master) + _, token, err := state.ACLTokenGetBySecret(nil, master, nil) if err != nil { return fmt.Errorf("failed to get master token: %v", err) } @@ -562,6 +563,7 @@ func (s *Server) initializeACLs(upgrade bool) error { Type: structs.ACLTokenTypeManagement, } + token.EnterpriseMeta.InitDefault() token.SetHash(true) done := false @@ -597,7 +599,7 @@ func (s *Server) initializeACLs(upgrade bool) error { } state := s.fsm.State() - _, token, err := state.ACLTokenGetBySecret(nil, structs.ACLTokenAnonymousID) + _, token, err := state.ACLTokenGetBySecret(nil, structs.ACLTokenAnonymousID, nil) if err != nil { return fmt.Errorf("failed to get anonymous token: %v", err) } @@ -605,7 +607,7 @@ func (s *Server) initializeACLs(upgrade bool) error { if token == nil { // DEPRECATED (ACL-Legacy-Compat) - Don't need to query for previous "anonymous" token // check for legacy token that needs an upgrade - _, legacyToken, err := state.ACLTokenGetBySecret(nil, anonymousToken) + _, legacyToken, err := state.ACLTokenGetBySecret(nil, anonymousToken, nil) if err != nil { return fmt.Errorf("failed to get anonymous token: %v", err) } @@ -620,6 +622,7 @@ func (s *Server) initializeACLs(upgrade bool) error { CreateTime: time.Now(), } token.SetHash(true) + token.EnterpriseMeta.InitDefault() req := structs.ACLTokenBatchSetRequest{ Tokens: structs.ACLTokens{token}, diff --git a/agent/consul/leader_test.go b/agent/consul/leader_test.go index 2d73a13121..73fcf77e73 100644 --- a/agent/consul/leader_test.go +++ b/agent/consul/leader_test.go @@ -1049,12 +1049,12 @@ func TestLeader_ACL_Initialization(t *testing.T) { testrpc.WaitForTestAgent(t, s1.RPC, "dc1") if tt.master != "" { - _, master, err := s1.fsm.State().ACLTokenGetBySecret(nil, tt.master) + _, master, err := s1.fsm.State().ACLTokenGetBySecret(nil, tt.master, nil) require.NoError(t, err) require.NotNil(t, master) } - _, anon, err := s1.fsm.State().ACLTokenGetBySecret(nil, anonymousToken) + _, anon, err := s1.fsm.State().ACLTokenGetBySecret(nil, anonymousToken, nil) require.NoError(t, err) require.NotNil(t, anon) @@ -1062,7 +1062,7 @@ func TestLeader_ACL_Initialization(t *testing.T) { require.NoError(t, err) require.Equal(t, tt.bootstrap, canBootstrap) - _, policy, err := s1.fsm.State().ACLPolicyGetByID(nil, structs.ACLPolicyGlobalManagementID) + _, policy, err := s1.fsm.State().ACLPolicyGetByID(nil, structs.ACLPolicyGlobalManagementID, nil) require.NoError(t, err) require.NotNil(t, policy) }) @@ -1096,7 +1096,7 @@ func TestLeader_ACLUpgrade(t *testing.T) { // wait for it to be upgraded retry.Run(t, func(t *retry.R) { - _, token, err := s1.fsm.State().ACLTokenGetBySecret(nil, mgmt_id) + _, token, err := s1.fsm.State().ACLTokenGetBySecret(nil, mgmt_id, nil) require.NoError(t, err) require.NotNil(t, token) require.NotEqual(t, "", token.AccessorID) @@ -1121,7 +1121,7 @@ func TestLeader_ACLUpgrade(t *testing.T) { // wait for it to be upgraded retry.Run(t, func(t *retry.R) { - _, token, err := s1.fsm.State().ACLTokenGetBySecret(nil, client_id) + _, token, err := s1.fsm.State().ACLTokenGetBySecret(nil, client_id, nil) require.NoError(t, err) require.NotNil(t, token) require.NotEqual(t, "", token.AccessorID) diff --git a/agent/consul/rpc.go b/agent/consul/rpc.go index 2f3158a7d5..573a4b6d09 100644 --- a/agent/consul/rpc.go +++ b/agent/consul/rpc.go @@ -10,7 +10,6 @@ import ( "time" "github.com/armon/go-metrics" - "github.com/hashicorp/consul/agent/agentpb" "github.com/hashicorp/consul/agent/consul/state" "github.com/hashicorp/consul/agent/metadata" "github.com/hashicorp/consul/agent/pool" @@ -388,7 +387,7 @@ func (s *Server) raftApplyMsgpack(t structs.MessageType, msg interface{}) (inter // raftApplyProtobuf will protobuf encode the request and then run it through raft, // then return the FSM response along with any errors. func (s *Server) raftApplyProtobuf(t structs.MessageType, msg interface{}) (interface{}, error) { - return s.raftApplyWithEncoder(t, msg, agentpb.EncodeInterface) + return s.raftApplyWithEncoder(t, msg, structs.EncodeProtoInterface) } // raftApplyWithEncoder is used to encode a message, run it through raft, diff --git a/agent/consul/server.go b/agent/consul/server.go index 412cd01dd3..8bcc8ed0b8 100644 --- a/agent/consul/server.go +++ b/agent/consul/server.go @@ -20,6 +20,7 @@ import ( metrics "github.com/armon/go-metrics" "github.com/hashicorp/consul/acl" ca "github.com/hashicorp/consul/agent/connect/ca" + "github.com/hashicorp/consul/agent/consul/authmethod" "github.com/hashicorp/consul/agent/consul/autopilot" "github.com/hashicorp/consul/agent/consul/fsm" "github.com/hashicorp/consul/agent/consul/state" @@ -114,8 +115,7 @@ type Server struct { // acls is used to resolve tokens to effective policies acls *ACLResolver - aclAuthMethodValidators map[string]*authMethodValidatorEntry - aclAuthMethodValidatorLock sync.RWMutex + aclAuthMethodValidators authmethod.Cache // DEPRECATED (ACL-Legacy-Compat) - only needed while we support both // useNewACLs is used to determine whether we can use new ACLs or not @@ -351,25 +351,26 @@ func NewServerLogger(config *Config, logger *log.Logger, tokens *token.Store, tl // Create server. s := &Server{ - config: config, - tokens: tokens, - connPool: connPool, - eventChLAN: make(chan serf.Event, serfEventChSize), - eventChWAN: make(chan serf.Event, serfEventChSize), - logger: logger, - leaveCh: make(chan struct{}), - reconcileCh: make(chan serf.Member, reconcileChSize), - router: router.NewRouter(logger, config.Datacenter), - rpcServer: rpc.NewServer(), - insecureRPCServer: rpc.NewServer(), - tlsConfigurator: tlsConfigurator, - reassertLeaderCh: make(chan chan error), - segmentLAN: make(map[string]*serf.Serf, len(config.Segments)), - sessionTimers: NewSessionTimers(), - tombstoneGC: gc, - serverLookup: NewServerLookup(), - shutdownCh: shutdownCh, - leaderRoutineManager: NewLeaderRoutineManager(logger), + config: config, + tokens: tokens, + connPool: connPool, + eventChLAN: make(chan serf.Event, serfEventChSize), + eventChWAN: make(chan serf.Event, serfEventChSize), + logger: logger, + leaveCh: make(chan struct{}), + reconcileCh: make(chan serf.Member, reconcileChSize), + router: router.NewRouter(logger, config.Datacenter), + rpcServer: rpc.NewServer(), + insecureRPCServer: rpc.NewServer(), + tlsConfigurator: tlsConfigurator, + reassertLeaderCh: make(chan chan error), + segmentLAN: make(map[string]*serf.Serf, len(config.Segments)), + sessionTimers: NewSessionTimers(), + tombstoneGC: gc, + serverLookup: NewServerLookup(), + shutdownCh: shutdownCh, + leaderRoutineManager: NewLeaderRoutineManager(logger), + aclAuthMethodValidators: authmethod.NewCache(), } // Initialize enterprise specific server functionality diff --git a/agent/consul/state/acl.go b/agent/consul/state/acl.go index c0a1d52c68..a452b9975c 100644 --- a/agent/consul/state/acl.go +++ b/agent/consul/state/acl.go @@ -211,195 +211,6 @@ func (s *TokenExpirationIndex) FromArgs(args ...interface{}) ([]byte, error) { return buf, nil } -func tokensTableSchema() *memdb.TableSchema { - return &memdb.TableSchema{ - Name: "acl-tokens", - Indexes: map[string]*memdb.IndexSchema{ - "accessor": &memdb.IndexSchema{ - Name: "accessor", - // DEPRECATED (ACL-Legacy-Compat) - we should not AllowMissing here once legacy compat is removed - AllowMissing: true, - Unique: true, - Indexer: &memdb.UUIDFieldIndex{ - Field: "AccessorID", - }, - }, - "id": &memdb.IndexSchema{ - Name: "id", - AllowMissing: false, - Unique: true, - Indexer: &memdb.StringFieldIndex{ - Field: "SecretID", - Lowercase: false, - }, - }, - "policies": &memdb.IndexSchema{ - Name: "policies", - // Need to allow missing for the anonymous token - AllowMissing: true, - Unique: false, - Indexer: &TokenPoliciesIndex{}, - }, - "roles": &memdb.IndexSchema{ - Name: "roles", - AllowMissing: true, - Unique: false, - Indexer: &TokenRolesIndex{}, - }, - "authmethod": &memdb.IndexSchema{ - Name: "authmethod", - AllowMissing: true, - Unique: false, - Indexer: &memdb.StringFieldIndex{ - Field: "AuthMethod", - Lowercase: false, - }, - }, - "local": &memdb.IndexSchema{ - Name: "local", - AllowMissing: false, - Unique: false, - Indexer: &memdb.ConditionalIndex{ - Conditional: func(obj interface{}) (bool, error) { - if token, ok := obj.(*structs.ACLToken); ok { - return token.Local, nil - } - return false, nil - }, - }, - }, - "expires-global": { - Name: "expires-global", - AllowMissing: true, - Unique: false, - Indexer: &TokenExpirationIndex{LocalFilter: false}, - }, - "expires-local": { - Name: "expires-local", - AllowMissing: true, - Unique: false, - Indexer: &TokenExpirationIndex{LocalFilter: true}, - }, - - //DEPRECATED (ACL-Legacy-Compat) - This index is only needed while we support upgrading v1 to v2 acls - // This table indexes all the ACL tokens that do not have an AccessorID - "needs-upgrade": &memdb.IndexSchema{ - Name: "needs-upgrade", - AllowMissing: false, - Unique: false, - Indexer: &memdb.ConditionalIndex{ - Conditional: func(obj interface{}) (bool, error) { - if token, ok := obj.(*structs.ACLToken); ok { - return token.AccessorID == "", nil - } - return false, nil - }, - }, - }, - }, - } -} - -func policiesTableSchema() *memdb.TableSchema { - return &memdb.TableSchema{ - Name: "acl-policies", - Indexes: map[string]*memdb.IndexSchema{ - "id": &memdb.IndexSchema{ - Name: "id", - AllowMissing: false, - Unique: true, - Indexer: &memdb.UUIDFieldIndex{ - Field: "ID", - }, - }, - "name": &memdb.IndexSchema{ - Name: "name", - AllowMissing: false, - Unique: true, - Indexer: &memdb.StringFieldIndex{ - Field: "Name", - // TODO (ACL-V2) - should we coerce to lowercase? - Lowercase: true, - }, - }, - }, - } -} - -func rolesTableSchema() *memdb.TableSchema { - return &memdb.TableSchema{ - Name: "acl-roles", - Indexes: map[string]*memdb.IndexSchema{ - "id": &memdb.IndexSchema{ - Name: "id", - AllowMissing: false, - Unique: true, - Indexer: &memdb.UUIDFieldIndex{ - Field: "ID", - }, - }, - "name": &memdb.IndexSchema{ - Name: "name", - AllowMissing: false, - Unique: true, - Indexer: &memdb.StringFieldIndex{ - Field: "Name", - Lowercase: true, - }, - }, - "policies": &memdb.IndexSchema{ - Name: "policies", - // Need to allow missing for the anonymous token - AllowMissing: true, - Unique: false, - Indexer: &RolePoliciesIndex{}, - }, - }, - } -} - -func bindingRulesTableSchema() *memdb.TableSchema { - return &memdb.TableSchema{ - Name: "acl-binding-rules", - Indexes: map[string]*memdb.IndexSchema{ - "id": &memdb.IndexSchema{ - Name: "id", - AllowMissing: false, - Unique: true, - Indexer: &memdb.UUIDFieldIndex{ - Field: "ID", - }, - }, - "authmethod": &memdb.IndexSchema{ - Name: "authmethod", - AllowMissing: false, - Unique: false, - Indexer: &memdb.StringFieldIndex{ - Field: "AuthMethod", - Lowercase: true, - }, - }, - }, - } -} - -func authMethodsTableSchema() *memdb.TableSchema { - return &memdb.TableSchema{ - Name: "acl-auth-methods", - Indexes: map[string]*memdb.IndexSchema{ - "id": &memdb.IndexSchema{ - Name: "id", - AllowMissing: false, - Unique: true, - Indexer: &memdb.StringFieldIndex{ - Field: "Name", - Lowercase: true, - }, - }, - }, - } -} - func init() { registerSchema(tokensTableSchema) registerSchema(policiesTableSchema) @@ -410,7 +221,6 @@ func init() { // ACLTokens is used when saving a snapshot func (s *Snapshot) ACLTokens() (memdb.ResultIterator, error) { - // DEPRECATED (ACL-Legacy-Compat) - This could use the "id" index when we remove v1 compat iter, err := s.tx.Get("acl-tokens", "id") if err != nil { return nil, err @@ -420,14 +230,7 @@ func (s *Snapshot) ACLTokens() (memdb.ResultIterator, error) { // ACLToken is used when restoring from a snapshot. For general inserts, use ACL. func (s *Restore) ACLToken(token *structs.ACLToken) error { - if err := s.tx.Insert("acl-tokens", token); err != nil { - return fmt.Errorf("failed restoring acl token: %s", err) - } - - if err := indexUpdateMaxTxn(s.tx, token.ModifyIndex, "acl-tokens"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } - return nil + return s.store.aclTokenInsert(s.tx, token, false) } // ACLPolicies is used when saving a snapshot @@ -440,14 +243,7 @@ func (s *Snapshot) ACLPolicies() (memdb.ResultIterator, error) { } func (s *Restore) ACLPolicy(policy *structs.ACLPolicy) error { - if err := s.tx.Insert("acl-policies", policy); err != nil { - return fmt.Errorf("failed restoring acl policy: %s", err) - } - - if err := indexUpdateMaxTxn(s.tx, policy.ModifyIndex, "acl-policies"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } - return nil + return s.store.aclPolicyInsert(s.tx, policy, false) } // ACLRoles is used when saving a snapshot @@ -460,14 +256,7 @@ func (s *Snapshot) ACLRoles() (memdb.ResultIterator, error) { } func (s *Restore) ACLRole(role *structs.ACLRole) error { - if err := s.tx.Insert("acl-roles", role); err != nil { - return fmt.Errorf("failed restoring acl role: %s", err) - } - - if err := indexUpdateMaxTxn(s.tx, role.ModifyIndex, "acl-roles"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } - return nil + return s.store.aclRoleInsert(s.tx, role, false) } // ACLBindingRules is used when saving a snapshot @@ -480,14 +269,7 @@ func (s *Snapshot) ACLBindingRules() (memdb.ResultIterator, error) { } func (s *Restore) ACLBindingRule(rule *structs.ACLBindingRule) error { - if err := s.tx.Insert("acl-binding-rules", rule); err != nil { - return fmt.Errorf("failed restoring acl binding rule: %s", err) - } - - if err := indexUpdateMaxTxn(s.tx, rule.ModifyIndex, "acl-binding-rules"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } - return nil + return s.store.aclBindingRuleInsert(s.tx, rule, false) } // ACLAuthMethods is used when saving a snapshot @@ -500,14 +282,7 @@ func (s *Snapshot) ACLAuthMethods() (memdb.ResultIterator, error) { } func (s *Restore) ACLAuthMethod(method *structs.ACLAuthMethod) error { - if err := s.tx.Insert("acl-auth-methods", method); err != nil { - return fmt.Errorf("failed restoring acl auth method: %s", err) - } - - if err := indexUpdateMaxTxn(s.tx, method.ModifyIndex, "acl-auth-methods"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } - return nil + return s.store.aclAuthMethodInsert(s.tx, method, false) } // ACLBootstrap is used to perform a one-time ACL bootstrap operation on a @@ -532,9 +307,6 @@ func (s *Store) ACLBootstrap(idx, resetIndex uint64, token *structs.ACLToken, le if err := s.aclTokenSetTxn(tx, idx, token, false, false, false, legacy); err != nil { return fmt.Errorf("failed inserting bootstrap token: %v", err) } - if err := indexUpdateMaxTxn(tx, idx, "acl-tokens"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } if err := tx.Insert("index", &IndexEntry{"acl-token-bootstrap", idx}); err != nil { return fmt.Errorf("failed to mark ACL bootstrapping as complete: %v", err) } @@ -638,7 +410,7 @@ func (s *Store) resolveTokenPolicyLinks(tx *memdb.Txn, token *structs.ACLToken, var numValid int for linkIndex, link := range token.Policies { if link.ID != "" { - policy, err := s.getPolicyWithTxn(tx, nil, link.ID, "id") + policy, err := s.getPolicyWithTxn(tx, nil, link.ID, s.aclPolicyGetByID, &token.EnterpriseMeta) if err != nil { return 0, err @@ -678,7 +450,7 @@ func (s *Store) fixupTokenPolicyLinks(tx *memdb.Txn, original *structs.ACLToken) return nil, fmt.Errorf("Detected corrupted token within the state store - missing policy link ID") } - policy, err := s.getPolicyWithTxn(tx, nil, link.ID, "id") + policy, err := s.getPolicyWithTxn(tx, nil, link.ID, s.aclPolicyGetByID, &token.EnterpriseMeta) if err != nil { return nil, err @@ -712,7 +484,7 @@ func (s *Store) resolveTokenRoleLinks(tx *memdb.Txn, token *structs.ACLToken, al var numValid int for linkIndex, link := range token.Roles { if link.ID != "" { - role, err := s.getRoleWithTxn(tx, nil, link.ID, "id") + role, err := s.getRoleWithTxn(tx, nil, link.ID, s.aclRoleGetByID, &token.EnterpriseMeta) if err != nil { return 0, err @@ -752,7 +524,7 @@ func (s *Store) fixupTokenRoleLinks(tx *memdb.Txn, original *structs.ACLToken) ( return nil, fmt.Errorf("Detected corrupted token within the state store - missing role link ID") } - role, err := s.getRoleWithTxn(tx, nil, link.ID, "id") + role, err := s.getRoleWithTxn(tx, nil, link.ID, s.aclRoleGetByID, &original.EnterpriseMeta) if err != nil { return nil, err @@ -785,7 +557,7 @@ func (s *Store) fixupTokenRoleLinks(tx *memdb.Txn, original *structs.ACLToken) ( func (s *Store) resolveRolePolicyLinks(tx *memdb.Txn, role *structs.ACLRole, allowMissing bool) error { for linkIndex, link := range role.Policies { if link.ID != "" { - policy, err := s.getPolicyWithTxn(tx, nil, link.ID, "id") + policy, err := s.getPolicyWithTxn(tx, nil, link.ID, s.aclPolicyGetByID, &role.EnterpriseMeta) if err != nil { return err @@ -824,7 +596,7 @@ func (s *Store) fixupRolePolicyLinks(tx *memdb.Txn, original *structs.ACLRole) ( return nil, fmt.Errorf("Detected corrupted role within the state store - missing policy link ID") } - policy, err := s.getPolicyWithTxn(tx, nil, link.ID, "id") + policy, err := s.getPolicyWithTxn(tx, nil, link.ID, s.aclPolicyGetByID, &original.EnterpriseMeta) if err != nil { return nil, err @@ -864,10 +636,6 @@ func (s *Store) ACLTokenSet(idx uint64, token *structs.ACLToken, legacy bool) er return err } - if err := indexUpdateMaxTxn(tx, idx, "acl-tokens"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } - tx.Commit() return nil } @@ -882,10 +650,6 @@ func (s *Store) ACLTokenBatchSet(idx uint64, tokens structs.ACLTokens, cas, allo } } - if err := indexUpdateMaxTxn(tx, idx, "acl-tokens"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } - tx.Commit() return nil } @@ -915,7 +679,7 @@ func (s *Store) aclTokenSetTxn(tx *memdb.Txn, idx uint64, token *structs.ACLToke // Check for an existing ACL // DEPRECATED (ACL-Legacy-Compat) - transition to using accessor index instead of secret once v1 compat is removed - existing, err := tx.First("acl-tokens", "id", token.SecretID) + _, existing, err := s.aclTokenGetFromIndex(tx, token.SecretID, "id", nil) if err != nil { return fmt.Errorf("failed token lookup: %s", err) } @@ -949,6 +713,10 @@ func (s *Store) aclTokenSetTxn(tx *memdb.Txn, idx uint64, token *structs.ACLToke token.AccessorID = original.AccessorID } + if err := s.aclTokenUpsertValidateEnterprise(tx, token, original); err != nil { + return err + } + var numValidPolicies int if numValidPolicies, err = s.resolveTokenPolicyLinks(tx, token, allowMissingPolicyAndRoleIDs); err != nil { return err @@ -960,7 +728,7 @@ func (s *Store) aclTokenSetTxn(tx *memdb.Txn, idx uint64, token *structs.ACLToke } if token.AuthMethod != "" { - method, err := s.getAuthMethodWithTxn(tx, nil, token.AuthMethod, "id") + method, err := s.getAuthMethodWithTxn(tx, nil, token.AuthMethod, &token.EnterpriseMeta) if err != nil { return err } else if method == nil { @@ -997,35 +765,30 @@ func (s *Store) aclTokenSetTxn(tx *memdb.Txn, idx uint64, token *structs.ACLToke token.ModifyIndex = idx } - // Insert the ACL - if err := tx.Insert("acl-tokens", token); err != nil { - return fmt.Errorf("failed inserting acl token: %v", err) - } - - return nil + return s.aclTokenInsert(tx, token, true) } // ACLTokenGetBySecret is used to look up an existing ACL token by its SecretID. -func (s *Store) ACLTokenGetBySecret(ws memdb.WatchSet, secret string) (uint64, *structs.ACLToken, error) { - return s.aclTokenGet(ws, secret, "id") +func (s *Store) ACLTokenGetBySecret(ws memdb.WatchSet, secret string, entMeta *structs.EnterpriseMeta) (uint64, *structs.ACLToken, error) { + return s.aclTokenGet(ws, secret, "id", entMeta) } // ACLTokenGetByAccessor is used to look up an existing ACL token by its AccessorID. -func (s *Store) ACLTokenGetByAccessor(ws memdb.WatchSet, accessor string) (uint64, *structs.ACLToken, error) { - return s.aclTokenGet(ws, accessor, "accessor") +func (s *Store) ACLTokenGetByAccessor(ws memdb.WatchSet, accessor string, entMeta *structs.EnterpriseMeta) (uint64, *structs.ACLToken, error) { + return s.aclTokenGet(ws, accessor, "accessor", entMeta) } // aclTokenGet looks up a token using one of the indexes provided -func (s *Store) aclTokenGet(ws memdb.WatchSet, value, index string) (uint64, *structs.ACLToken, error) { +func (s *Store) aclTokenGet(ws memdb.WatchSet, value, index string, entMeta *structs.EnterpriseMeta) (uint64, *structs.ACLToken, error) { tx := s.db.Txn(false) defer tx.Abort() - token, err := s.aclTokenGetTxn(tx, ws, value, index) + token, err := s.aclTokenGetTxn(tx, ws, value, index, entMeta) if err != nil { - return 0, nil, fmt.Errorf("failed acl token lookup: %v", err) + return 0, nil, err } - idx := maxIndexTxn(tx, "acl-tokens") + idx := s.aclTokenMaxIndex(tx, token, entMeta) return idx, token, nil } @@ -1035,7 +798,7 @@ func (s *Store) ACLTokenBatchGet(ws memdb.WatchSet, accessors []string) (uint64, tokens := make(structs.ACLTokens, 0) for _, accessor := range accessors { - token, err := s.aclTokenGetTxn(tx, ws, accessor, "accessor") + token, err := s.aclTokenGetTxn(tx, ws, accessor, "accessor", nil) if err != nil { return 0, nil, fmt.Errorf("failed acl token lookup: %v", err) } @@ -1051,8 +814,8 @@ func (s *Store) ACLTokenBatchGet(ws memdb.WatchSet, accessors []string) (uint64, return idx, tokens, nil } -func (s *Store) aclTokenGetTxn(tx *memdb.Txn, ws memdb.WatchSet, value, index string) (*structs.ACLToken, error) { - watchCh, rawToken, err := tx.FirstWatch("acl-tokens", index, value) +func (s *Store) aclTokenGetTxn(tx *memdb.Txn, ws memdb.WatchSet, value, index string, entMeta *structs.EnterpriseMeta) (*structs.ACLToken, error) { + watchCh, rawToken, err := s.aclTokenGetFromIndex(tx, value, index, entMeta) if err != nil { return nil, fmt.Errorf("failed acl token lookup: %v", err) } @@ -1075,7 +838,7 @@ func (s *Store) aclTokenGetTxn(tx *memdb.Txn, ws memdb.WatchSet, value, index st } // ACLTokenList is used to list out all of the ACLs in the state store. -func (s *Store) ACLTokenList(ws memdb.WatchSet, local, global bool, policy, role, methodName string) (uint64, structs.ACLTokens, error) { +func (s *Store) ACLTokenList(ws memdb.WatchSet, local, global bool, policy, role, methodName string, entMeta *structs.EnterpriseMeta) (uint64, structs.ACLTokens, error) { tx := s.db.Txn(false) defer tx.Abort() @@ -1089,23 +852,23 @@ func (s *Store) ACLTokenList(ws memdb.WatchSet, local, global bool, policy, role needLocalityFilter := false if policy == "" && role == "" && methodName == "" { if global == local { - iter, err = tx.Get("acl-tokens", "id") + iter, err = s.aclTokenListAll(tx, entMeta) } else if global { - iter, err = tx.Get("acl-tokens", "local", false) + iter, err = s.aclTokenListGlobal(tx, entMeta) } else { - iter, err = tx.Get("acl-tokens", "local", true) + iter, err = s.aclTokenListLocal(tx, entMeta) } } else if policy != "" && role == "" && methodName == "" { - iter, err = tx.Get("acl-tokens", "policies", policy) + iter, err = s.aclTokenListByPolicy(tx, policy, entMeta) needLocalityFilter = true } else if policy == "" && role != "" && methodName == "" { - iter, err = tx.Get("acl-tokens", "roles", role) + iter, err = s.aclTokenListByRole(tx, role, entMeta) needLocalityFilter = true } else if policy == "" && role == "" && methodName != "" { - iter, err = tx.Get("acl-tokens", "authmethod", methodName) + iter, err = s.aclTokenListByAuthMethod(tx, methodName, entMeta) needLocalityFilter = true } else { @@ -1150,7 +913,7 @@ func (s *Store) ACLTokenList(ws memdb.WatchSet, local, global bool, policy, role } // Get the table index. - idx := maxIndexTxn(tx, "acl-tokens") + idx := s.aclTokenMaxIndex(tx, nil, entMeta) return idx, result, nil } @@ -1235,14 +998,14 @@ func (s *Store) expiresIndexName(local bool) string { // ACLTokenDeleteBySecret is used to remove an existing ACL from the state store. If // the ACL does not exist this is a no-op and no error is returned. -func (s *Store) ACLTokenDeleteBySecret(idx uint64, secret string) error { - return s.aclTokenDelete(idx, secret, "id") +func (s *Store) ACLTokenDeleteBySecret(idx uint64, secret string, entMeta *structs.EnterpriseMeta) error { + return s.aclTokenDelete(idx, secret, "id", entMeta) } // ACLTokenDeleteByAccessor is used to remove an existing ACL from the state store. If // the ACL does not exist this is a no-op and no error is returned. -func (s *Store) ACLTokenDeleteByAccessor(idx uint64, accessor string) error { - return s.aclTokenDelete(idx, accessor, "accessor") +func (s *Store) ACLTokenDeleteByAccessor(idx uint64, accessor string, entMeta *structs.EnterpriseMeta) error { + return s.aclTokenDelete(idx, accessor, "accessor", entMeta) } func (s *Store) ACLTokenBatchDelete(idx uint64, tokenIDs []string) error { @@ -1250,7 +1013,7 @@ func (s *Store) ACLTokenBatchDelete(idx uint64, tokenIDs []string) error { defer tx.Abort() for _, tokenID := range tokenIDs { - if err := s.aclTokenDeleteTxn(tx, idx, tokenID, "accessor"); err != nil { + if err := s.aclTokenDeleteTxn(tx, idx, tokenID, "accessor", nil); err != nil { return err } } @@ -1259,11 +1022,11 @@ func (s *Store) ACLTokenBatchDelete(idx uint64, tokenIDs []string) error { return nil } -func (s *Store) aclTokenDelete(idx uint64, value, index string) error { +func (s *Store) aclTokenDelete(idx uint64, value, index string, entMeta *structs.EnterpriseMeta) error { tx := s.db.Txn(true) defer tx.Abort() - if err := s.aclTokenDeleteTxn(tx, idx, value, index); err != nil { + if err := s.aclTokenDeleteTxn(tx, idx, value, index, entMeta); err != nil { return err } @@ -1271,9 +1034,9 @@ func (s *Store) aclTokenDelete(idx uint64, value, index string) error { return nil } -func (s *Store) aclTokenDeleteTxn(tx *memdb.Txn, idx uint64, value, index string) error { +func (s *Store) aclTokenDeleteTxn(tx *memdb.Txn, idx uint64, value, index string, entMeta *structs.EnterpriseMeta) error { // Look up the existing token - token, err := tx.First("acl-tokens", index, value) + _, token, err := s.aclTokenGetFromIndex(tx, value, index, entMeta) if err != nil { return fmt.Errorf("failed acl token lookup: %v", err) } @@ -1286,18 +1049,12 @@ func (s *Store) aclTokenDeleteTxn(tx *memdb.Txn, idx uint64, value, index string return fmt.Errorf("Deletion of the builtin anonymous token is not permitted") } - if err := tx.Delete("acl-tokens", token); err != nil { - return fmt.Errorf("failed deleting acl token: %v", err) - } - if err := indexUpdateMaxTxn(tx, idx, "acl-tokens"); err != nil { - return fmt.Errorf("failed updating index: %v", err) - } - return nil + return s.aclTokenDeleteWithToken(tx, token.(*structs.ACLToken), idx) } -func (s *Store) aclTokenDeleteAllForAuthMethodTxn(tx *memdb.Txn, idx uint64, methodName string) error { +func (s *Store) aclTokenDeleteAllForAuthMethodTxn(tx *memdb.Txn, idx uint64, methodName string, entMeta *structs.EnterpriseMeta) error { // collect them all - iter, err := tx.Get("acl-tokens", "authmethod", methodName) + iter, err := s.aclTokenListByAuthMethod(tx, methodName, entMeta) if err != nil { return fmt.Errorf("failed acl token lookup: %v", err) } @@ -1311,14 +1068,10 @@ func (s *Store) aclTokenDeleteAllForAuthMethodTxn(tx *memdb.Txn, idx uint64, met if len(tokens) > 0 { // delete them all for _, token := range tokens { - if err := tx.Delete("acl-tokens", token); err != nil { - return fmt.Errorf("failed deleting acl token: %v", err) + if err := s.aclTokenDeleteWithToken(tx, token, idx); err != nil { + return err } } - - if err := indexUpdateMaxTxn(tx, idx, "acl-tokens"); err != nil { - return fmt.Errorf("failed updating index: %v", err) - } } return nil @@ -1334,10 +1087,6 @@ func (s *Store) ACLPolicyBatchSet(idx uint64, policies structs.ACLPolicies) erro } } - if err := indexUpdateMaxTxn(tx, idx, "acl-policies"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } - tx.Commit() return nil } @@ -1349,9 +1098,6 @@ func (s *Store) ACLPolicySet(idx uint64, policy *structs.ACLPolicy) error { if err := s.aclPolicySetTxn(tx, idx, policy); err != nil { return err } - if err := indexUpdateMaxTxn(tx, idx, "acl-policies"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } tx.Commit() return nil @@ -1367,9 +1113,14 @@ func (s *Store) aclPolicySetTxn(tx *memdb.Txn, idx uint64, policy *structs.ACLPo return ErrMissingACLPolicyName } - existing, err := tx.First("acl-policies", "id", policy.ID) + var existing *structs.ACLPolicy + _, existingRaw, err := s.aclPolicyGetByID(tx, policy.ID, nil) if err != nil { - return fmt.Errorf("failed acl policy lookup: %v", err) + return err + } + + if existingRaw != nil { + existing = existingRaw.(*structs.ACLPolicy) } if existing != nil { @@ -1390,17 +1141,21 @@ func (s *Store) aclPolicySetTxn(tx *memdb.Txn, idx uint64, policy *structs.ACLPo } // ensure the name is unique (cannot conflict with another policy with a different ID) - nameMatch, err := tx.First("acl-policies", "name", policy.Name) + _, nameMatch, err := s.aclPolicyGetByName(tx, policy.Name, &policy.EnterpriseMeta) if err != nil { - return fmt.Errorf("failed acl policy lookup: %v", err) + return err } if nameMatch != nil && policy.ID != nameMatch.(*structs.ACLPolicy).ID { return fmt.Errorf("A policy with name %q already exists", policy.Name) } + if err := s.aclPolicyUpsertValidateEnterprise(tx, policy, existing); err != nil { + return err + } + // Set the indexes if existing != nil { - policy.CreateIndex = existing.(*structs.ACLPolicy).CreateIndex + policy.CreateIndex = existing.CreateIndex policy.ModifyIndex = idx } else { policy.CreateIndex = idx @@ -1408,18 +1163,15 @@ func (s *Store) aclPolicySetTxn(tx *memdb.Txn, idx uint64, policy *structs.ACLPo } // Insert the ACL - if err := tx.Insert("acl-policies", policy); err != nil { - return fmt.Errorf("failed inserting acl policy: %v", err) - } - return nil + return s.aclPolicyInsert(tx, policy, true) } -func (s *Store) ACLPolicyGetByID(ws memdb.WatchSet, id string) (uint64, *structs.ACLPolicy, error) { - return s.aclPolicyGet(ws, id, "id") +func (s *Store) ACLPolicyGetByID(ws memdb.WatchSet, id string, entMeta *structs.EnterpriseMeta) (uint64, *structs.ACLPolicy, error) { + return s.aclPolicyGet(ws, id, s.aclPolicyGetByID, entMeta) } -func (s *Store) ACLPolicyGetByName(ws memdb.WatchSet, name string) (uint64, *structs.ACLPolicy, error) { - return s.aclPolicyGet(ws, name, "name") +func (s *Store) ACLPolicyGetByName(ws memdb.WatchSet, name string, entMeta *structs.EnterpriseMeta) (uint64, *structs.ACLPolicy, error) { + return s.aclPolicyGet(ws, name, s.aclPolicyGetByName, entMeta) } func (s *Store) ACLPolicyBatchGet(ws memdb.WatchSet, ids []string) (uint64, structs.ACLPolicies, error) { @@ -1428,7 +1180,7 @@ func (s *Store) ACLPolicyBatchGet(ws memdb.WatchSet, ids []string) (uint64, stru policies := make(structs.ACLPolicies, 0) for _, pid := range ids { - policy, err := s.getPolicyWithTxn(tx, ws, pid, "id") + policy, err := s.getPolicyWithTxn(tx, ws, pid, s.aclPolicyGetByID, nil) if err != nil { return 0, nil, err } @@ -1438,13 +1190,17 @@ func (s *Store) ACLPolicyBatchGet(ws memdb.WatchSet, ids []string) (uint64, stru } } + // We are specifically not wanting to call aclPolicyMaxIndex here as we always want the + // index entry for the "acl-policies" table. idx := maxIndexTxn(tx, "acl-policies") return idx, policies, nil } -func (s *Store) getPolicyWithTxn(tx *memdb.Txn, ws memdb.WatchSet, value, index string) (*structs.ACLPolicy, error) { - watchCh, policy, err := tx.FirstWatch("acl-policies", index, value) +type aclPolicyGetFn func(*memdb.Txn, string, *structs.EnterpriseMeta) (<-chan struct{}, interface{}, error) + +func (s *Store) getPolicyWithTxn(tx *memdb.Txn, ws memdb.WatchSet, value string, fn aclPolicyGetFn, entMeta *structs.EnterpriseMeta) (*structs.ACLPolicy, error) { + watchCh, policy, err := fn(tx, value, entMeta) if err != nil { return nil, fmt.Errorf("failed acl policy lookup: %v", err) } @@ -1457,25 +1213,25 @@ func (s *Store) getPolicyWithTxn(tx *memdb.Txn, ws memdb.WatchSet, value, index return policy.(*structs.ACLPolicy), nil } -func (s *Store) aclPolicyGet(ws memdb.WatchSet, value, index string) (uint64, *structs.ACLPolicy, error) { +func (s *Store) aclPolicyGet(ws memdb.WatchSet, value string, fn aclPolicyGetFn, entMeta *structs.EnterpriseMeta) (uint64, *structs.ACLPolicy, error) { tx := s.db.Txn(false) defer tx.Abort() - policy, err := s.getPolicyWithTxn(tx, ws, value, index) + policy, err := s.getPolicyWithTxn(tx, ws, value, fn, entMeta) if err != nil { return 0, nil, err } - idx := maxIndexTxn(tx, "acl-policies") + idx := s.aclPolicyMaxIndex(tx, policy, entMeta) return idx, policy, nil } -func (s *Store) ACLPolicyList(ws memdb.WatchSet) (uint64, structs.ACLPolicies, error) { +func (s *Store) ACLPolicyList(ws memdb.WatchSet, entMeta *structs.EnterpriseMeta) (uint64, structs.ACLPolicies, error) { tx := s.db.Txn(false) defer tx.Abort() - iter, err := tx.Get("acl-policies", "id") + iter, err := s.aclPolicyList(tx, entMeta) if err != nil { return 0, nil, fmt.Errorf("failed acl policy lookup: %v", err) } @@ -1487,17 +1243,17 @@ func (s *Store) ACLPolicyList(ws memdb.WatchSet) (uint64, structs.ACLPolicies, e } // Get the table index. - idx := maxIndexTxn(tx, "acl-policies") + idx := s.aclPolicyMaxIndex(tx, nil, entMeta) return idx, result, nil } -func (s *Store) ACLPolicyDeleteByID(idx uint64, id string) error { - return s.aclPolicyDelete(idx, id, "id") +func (s *Store) ACLPolicyDeleteByID(idx uint64, id string, entMeta *structs.EnterpriseMeta) error { + return s.aclPolicyDelete(idx, id, s.aclPolicyGetByID, entMeta) } -func (s *Store) ACLPolicyDeleteByName(idx uint64, name string) error { - return s.aclPolicyDelete(idx, name, "name") +func (s *Store) ACLPolicyDeleteByName(idx uint64, name string, entMeta *structs.EnterpriseMeta) error { + return s.aclPolicyDelete(idx, name, s.aclPolicyGetByName, entMeta) } func (s *Store) ACLPolicyBatchDelete(idx uint64, policyIDs []string) error { @@ -1505,36 +1261,29 @@ func (s *Store) ACLPolicyBatchDelete(idx uint64, policyIDs []string) error { defer tx.Abort() for _, policyID := range policyIDs { - if err := s.aclPolicyDeleteTxn(tx, idx, policyID, "id"); err != nil { + if err := s.aclPolicyDeleteTxn(tx, idx, policyID, s.aclPolicyGetByID, nil); err != nil { return err } } - - if err := indexUpdateMaxTxn(tx, idx, "acl-policies"); err != nil { - return fmt.Errorf("failed updating index: %v", err) - } tx.Commit() return nil } -func (s *Store) aclPolicyDelete(idx uint64, value, index string) error { +func (s *Store) aclPolicyDelete(idx uint64, value string, fn aclPolicyGetFn, entMeta *structs.EnterpriseMeta) error { tx := s.db.Txn(true) defer tx.Abort() - if err := s.aclPolicyDeleteTxn(tx, idx, value, index); err != nil { + if err := s.aclPolicyDeleteTxn(tx, idx, value, fn, entMeta); err != nil { return err } - if err := indexUpdateMaxTxn(tx, idx, "acl-policies"); err != nil { - return fmt.Errorf("failed updating index: %v", err) - } tx.Commit() return nil } -func (s *Store) aclPolicyDeleteTxn(tx *memdb.Txn, idx uint64, value, index string) error { +func (s *Store) aclPolicyDeleteTxn(tx *memdb.Txn, idx uint64, value string, fn aclPolicyGetFn, entMeta *structs.EnterpriseMeta) error { // Look up the existing token - rawPolicy, err := tx.First("acl-policies", index, value) + _, rawPolicy, err := fn(tx, value, entMeta) if err != nil { return fmt.Errorf("failed acl policy lookup: %v", err) } @@ -1549,10 +1298,7 @@ func (s *Store) aclPolicyDeleteTxn(tx *memdb.Txn, idx uint64, value, index strin return fmt.Errorf("Deletion of the builtin global-management policy is not permitted") } - if err := tx.Delete("acl-policies", policy); err != nil { - return fmt.Errorf("failed deleting acl policy: %v", err) - } - return nil + return s.aclPolicyDeleteWithPolicy(tx, policy, idx) } func (s *Store) ACLRoleBatchSet(idx uint64, roles structs.ACLRoles, allowMissingPolicyIDs bool) error { @@ -1565,10 +1311,6 @@ func (s *Store) ACLRoleBatchSet(idx uint64, roles structs.ACLRoles, allowMissing } } - if err := indexUpdateMaxTxn(tx, idx, "acl-roles"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } - tx.Commit() return nil } @@ -1580,9 +1322,6 @@ func (s *Store) ACLRoleSet(idx uint64, role *structs.ACLRole) error { if err := s.aclRoleSetTxn(tx, idx, role, false); err != nil { return err } - if err := indexUpdateMaxTxn(tx, idx, "acl-roles"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } tx.Commit() return nil @@ -1598,13 +1337,18 @@ func (s *Store) aclRoleSetTxn(tx *memdb.Txn, idx uint64, role *structs.ACLRole, return ErrMissingACLRoleName } - existing, err := tx.First("acl-roles", "id", role.ID) + _, existingRaw, err := s.aclRoleGetByID(tx, role.ID, nil) if err != nil { return fmt.Errorf("failed acl role lookup: %v", err) } + var existing *structs.ACLRole + if existingRaw != nil { + existing = existingRaw.(*structs.ACLRole) + } + // ensure the name is unique (cannot conflict with another role with a different ID) - nameMatch, err := tx.First("acl-roles", "name", role.Name) + _, nameMatch, err := s.aclRoleGetByName(tx, role.Name, &role.EnterpriseMeta) if err != nil { return fmt.Errorf("failed acl role lookup: %v", err) } @@ -1622,27 +1366,30 @@ func (s *Store) aclRoleSetTxn(tx *memdb.Txn, idx uint64, role *structs.ACLRole, } } + if err := s.aclRoleUpsertValidateEnterprise(tx, role, existing); err != nil { + return err + } + // Set the indexes if existing != nil { - role.CreateIndex = existing.(*structs.ACLRole).CreateIndex + role.CreateIndex = existing.CreateIndex role.ModifyIndex = idx } else { role.CreateIndex = idx role.ModifyIndex = idx } - if err := tx.Insert("acl-roles", role); err != nil { - return fmt.Errorf("failed inserting acl role: %v", err) - } - return nil + return s.aclRoleInsert(tx, role, true) } -func (s *Store) ACLRoleGetByID(ws memdb.WatchSet, id string) (uint64, *structs.ACLRole, error) { - return s.aclRoleGet(ws, id, "id") +type aclRoleGetFn func(*memdb.Txn, string, *structs.EnterpriseMeta) (<-chan struct{}, interface{}, error) + +func (s *Store) ACLRoleGetByID(ws memdb.WatchSet, id string, entMeta *structs.EnterpriseMeta) (uint64, *structs.ACLRole, error) { + return s.aclRoleGet(ws, id, s.aclRoleGetByID, entMeta) } -func (s *Store) ACLRoleGetByName(ws memdb.WatchSet, name string) (uint64, *structs.ACLRole, error) { - return s.aclRoleGet(ws, name, "name") +func (s *Store) ACLRoleGetByName(ws memdb.WatchSet, name string, entMeta *structs.EnterpriseMeta) (uint64, *structs.ACLRole, error) { + return s.aclRoleGet(ws, name, s.aclRoleGetByName, entMeta) } func (s *Store) ACLRoleBatchGet(ws memdb.WatchSet, ids []string) (uint64, structs.ACLRoles, error) { @@ -1651,7 +1398,7 @@ func (s *Store) ACLRoleBatchGet(ws memdb.WatchSet, ids []string) (uint64, struct roles := make(structs.ACLRoles, 0, len(ids)) for _, rid := range ids { - role, err := s.getRoleWithTxn(tx, ws, rid, "id") + role, err := s.getRoleWithTxn(tx, ws, rid, s.aclRoleGetByID, nil) if err != nil { return 0, nil, err } @@ -1666,8 +1413,8 @@ func (s *Store) ACLRoleBatchGet(ws memdb.WatchSet, ids []string) (uint64, struct return idx, roles, nil } -func (s *Store) getRoleWithTxn(tx *memdb.Txn, ws memdb.WatchSet, value, index string) (*structs.ACLRole, error) { - watchCh, rawRole, err := tx.FirstWatch("acl-roles", index, value) +func (s *Store) getRoleWithTxn(tx *memdb.Txn, ws memdb.WatchSet, value string, fn aclRoleGetFn, entMeta *structs.EnterpriseMeta) (*structs.ACLRole, error) { + watchCh, rawRole, err := fn(tx, value, entMeta) if err != nil { return nil, fmt.Errorf("failed acl role lookup: %v", err) } @@ -1685,21 +1432,21 @@ func (s *Store) getRoleWithTxn(tx *memdb.Txn, ws memdb.WatchSet, value, index st return nil, nil } -func (s *Store) aclRoleGet(ws memdb.WatchSet, value, index string) (uint64, *structs.ACLRole, error) { +func (s *Store) aclRoleGet(ws memdb.WatchSet, value string, fn aclRoleGetFn, entMeta *structs.EnterpriseMeta) (uint64, *structs.ACLRole, error) { tx := s.db.Txn(false) defer tx.Abort() - role, err := s.getRoleWithTxn(tx, ws, value, index) + role, err := s.getRoleWithTxn(tx, ws, value, fn, entMeta) if err != nil { return 0, nil, err } - idx := maxIndexTxn(tx, "acl-roles") + idx := s.aclRoleMaxIndex(tx, role, entMeta) return idx, role, nil } -func (s *Store) ACLRoleList(ws memdb.WatchSet, policy string) (uint64, structs.ACLRoles, error) { +func (s *Store) ACLRoleList(ws memdb.WatchSet, policy string, entMeta *structs.EnterpriseMeta) (uint64, structs.ACLRoles, error) { tx := s.db.Txn(false) defer tx.Abort() @@ -1707,9 +1454,9 @@ func (s *Store) ACLRoleList(ws memdb.WatchSet, policy string) (uint64, structs.A var err error if policy != "" { - iter, err = tx.Get("acl-roles", "policies", policy) + iter, err = s.aclRoleListByPolicy(tx, policy, entMeta) } else { - iter, err = tx.Get("acl-roles", "id") + iter, err = s.aclRoleList(tx, entMeta) } if err != nil { @@ -1728,17 +1475,17 @@ func (s *Store) ACLRoleList(ws memdb.WatchSet, policy string) (uint64, structs.A } // Get the table index. - idx := maxIndexTxn(tx, "acl-roles") + idx := s.aclRoleMaxIndex(tx, nil, entMeta) return idx, result, nil } -func (s *Store) ACLRoleDeleteByID(idx uint64, id string) error { - return s.aclRoleDelete(idx, id, "id") +func (s *Store) ACLRoleDeleteByID(idx uint64, id string, entMeta *structs.EnterpriseMeta) error { + return s.aclRoleDelete(idx, id, s.aclRoleGetByID, entMeta) } -func (s *Store) ACLRoleDeleteByName(idx uint64, name string) error { - return s.aclRoleDelete(idx, name, "name") +func (s *Store) ACLRoleDeleteByName(idx uint64, name string, entMeta *structs.EnterpriseMeta) error { + return s.aclRoleDelete(idx, name, s.aclRoleGetByName, entMeta) } func (s *Store) ACLRoleBatchDelete(idx uint64, roleIDs []string) error { @@ -1746,36 +1493,29 @@ func (s *Store) ACLRoleBatchDelete(idx uint64, roleIDs []string) error { defer tx.Abort() for _, roleID := range roleIDs { - if err := s.aclRoleDeleteTxn(tx, idx, roleID, "id"); err != nil { + if err := s.aclRoleDeleteTxn(tx, idx, roleID, s.aclRoleGetByID, nil); err != nil { return err } } - - if err := indexUpdateMaxTxn(tx, idx, "acl-roles"); err != nil { - return fmt.Errorf("failed updating index: %v", err) - } tx.Commit() return nil } -func (s *Store) aclRoleDelete(idx uint64, value, index string) error { +func (s *Store) aclRoleDelete(idx uint64, value string, fn aclRoleGetFn, entMeta *structs.EnterpriseMeta) error { tx := s.db.Txn(true) defer tx.Abort() - if err := s.aclRoleDeleteTxn(tx, idx, value, index); err != nil { + if err := s.aclRoleDeleteTxn(tx, idx, value, fn, entMeta); err != nil { return err } - if err := indexUpdateMaxTxn(tx, idx, "acl-roles"); err != nil { - return fmt.Errorf("failed updating index: %v", err) - } tx.Commit() return nil } -func (s *Store) aclRoleDeleteTxn(tx *memdb.Txn, idx uint64, value, index string) error { +func (s *Store) aclRoleDeleteTxn(tx *memdb.Txn, idx uint64, value string, fn aclRoleGetFn, entMeta *structs.EnterpriseMeta) error { // Look up the existing role - rawRole, err := tx.First("acl-roles", index, value) + _, rawRole, err := fn(tx, value, entMeta) if err != nil { return fmt.Errorf("failed acl role lookup: %v", err) } @@ -1786,10 +1526,7 @@ func (s *Store) aclRoleDeleteTxn(tx *memdb.Txn, idx uint64, value, index string) role := rawRole.(*structs.ACLRole) - if err := tx.Delete("acl-roles", role); err != nil { - return fmt.Errorf("failed deleting acl role: %v", err) - } - return nil + return s.aclRoleDeleteWithRole(tx, role, idx) } func (s *Store) ACLBindingRuleBatchSet(idx uint64, rules structs.ACLBindingRules) error { @@ -1802,10 +1539,6 @@ func (s *Store) ACLBindingRuleBatchSet(idx uint64, rules structs.ACLBindingRules } } - if err := indexUpdateMaxTxn(tx, idx, "acl-binding-rules"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } - tx.Commit() return nil } @@ -1817,10 +1550,6 @@ func (s *Store) ACLBindingRuleSet(idx uint64, rule *structs.ACLBindingRule) erro if err := s.aclBindingRuleSetTxn(tx, idx, rule); err != nil { return err } - if err := indexUpdateMaxTxn(tx, idx, "acl-binding-rules"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } - tx.Commit() return nil } @@ -1833,41 +1562,44 @@ func (s *Store) aclBindingRuleSetTxn(tx *memdb.Txn, idx uint64, rule *structs.AC return ErrMissingACLBindingRuleAuthMethod } - existing, err := tx.First("acl-binding-rules", "id", rule.ID) + var existing *structs.ACLBindingRule + _, existingRaw, err := s.aclBindingRuleGetByID(tx, rule.ID, nil) if err != nil { return fmt.Errorf("failed acl binding rule lookup: %v", err) } // Set the indexes - if existing != nil { - rule.CreateIndex = existing.(*structs.ACLBindingRule).CreateIndex + if existingRaw != nil { + existing = existingRaw.(*structs.ACLBindingRule) + rule.CreateIndex = existing.CreateIndex rule.ModifyIndex = idx } else { rule.CreateIndex = idx rule.ModifyIndex = idx } - if method, err := tx.First("acl-auth-methods", "id", rule.AuthMethod); err != nil { + if err := s.aclBindingRuleUpsertValidateEnterprise(tx, rule, existing); err != nil { + return err + } + + if _, method, err := s.aclAuthMethodGetByName(tx, rule.AuthMethod, &rule.EnterpriseMeta); err != nil { return fmt.Errorf("failed acl auth method lookup: %v", err) } else if method == nil { return fmt.Errorf("failed inserting acl binding rule: auth method not found") } - if err := tx.Insert("acl-binding-rules", rule); err != nil { - return fmt.Errorf("failed inserting acl binding rule: %v", err) - } - return nil + return s.aclBindingRuleInsert(tx, rule, true) } -func (s *Store) ACLBindingRuleGetByID(ws memdb.WatchSet, id string) (uint64, *structs.ACLBindingRule, error) { - return s.aclBindingRuleGet(ws, id, "id") +func (s *Store) ACLBindingRuleGetByID(ws memdb.WatchSet, id string, entMeta *structs.EnterpriseMeta) (uint64, *structs.ACLBindingRule, error) { + return s.aclBindingRuleGet(ws, id, entMeta) } -func (s *Store) aclBindingRuleGet(ws memdb.WatchSet, value, index string) (uint64, *structs.ACLBindingRule, error) { +func (s *Store) aclBindingRuleGet(ws memdb.WatchSet, value string, entMeta *structs.EnterpriseMeta) (uint64, *structs.ACLBindingRule, error) { tx := s.db.Txn(false) defer tx.Abort() - watchCh, rawRule, err := tx.FirstWatch("acl-binding-rules", index, value) + watchCh, rawRule, err := s.aclBindingRuleGetByID(tx, value, entMeta) if err != nil { return 0, nil, fmt.Errorf("failed acl binding rule lookup: %v", err) } @@ -1878,12 +1610,12 @@ func (s *Store) aclBindingRuleGet(ws memdb.WatchSet, value, index string) (uint6 rule = rawRule.(*structs.ACLBindingRule) } - idx := maxIndexTxn(tx, "acl-binding-rules") + idx := s.aclBindingRuleMaxIndex(tx, rule, entMeta) return idx, rule, nil } -func (s *Store) ACLBindingRuleList(ws memdb.WatchSet, methodName string) (uint64, structs.ACLBindingRules, error) { +func (s *Store) ACLBindingRuleList(ws memdb.WatchSet, methodName string, entMeta *structs.EnterpriseMeta) (uint64, structs.ACLBindingRules, error) { tx := s.db.Txn(false) defer tx.Abort() @@ -1892,9 +1624,9 @@ func (s *Store) ACLBindingRuleList(ws memdb.WatchSet, methodName string) (uint64 err error ) if methodName != "" { - iter, err = tx.Get("acl-binding-rules", "authmethod", methodName) + iter, err = s.aclBindingRuleListByAuthMethod(tx, methodName, entMeta) } else { - iter, err = tx.Get("acl-binding-rules", "id") + iter, err = s.aclBindingRuleList(tx, entMeta) } if err != nil { return 0, nil, fmt.Errorf("failed acl binding rule lookup: %v", err) @@ -1908,13 +1640,13 @@ func (s *Store) ACLBindingRuleList(ws memdb.WatchSet, methodName string) (uint64 } // Get the table index. - idx := maxIndexTxn(tx, "acl-binding-rules") + idx := s.aclBindingRuleMaxIndex(tx, nil, entMeta) return idx, result, nil } -func (s *Store) ACLBindingRuleDeleteByID(idx uint64, id string) error { - return s.aclBindingRuleDelete(idx, id, "id") +func (s *Store) ACLBindingRuleDeleteByID(idx uint64, id string, entMeta *structs.EnterpriseMeta) error { + return s.aclBindingRuleDelete(idx, id, entMeta) } func (s *Store) ACLBindingRuleBatchDelete(idx uint64, bindingRuleIDs []string) error { @@ -1922,34 +1654,27 @@ func (s *Store) ACLBindingRuleBatchDelete(idx uint64, bindingRuleIDs []string) e defer tx.Abort() for _, bindingRuleID := range bindingRuleIDs { - s.aclBindingRuleDeleteTxn(tx, idx, bindingRuleID, "id") - } - - if err := indexUpdateMaxTxn(tx, idx, "acl-binding-rules"); err != nil { - return fmt.Errorf("failed updating index: %v", err) + s.aclBindingRuleDeleteTxn(tx, idx, bindingRuleID, nil) } tx.Commit() return nil } -func (s *Store) aclBindingRuleDelete(idx uint64, value, index string) error { +func (s *Store) aclBindingRuleDelete(idx uint64, id string, entMeta *structs.EnterpriseMeta) error { tx := s.db.Txn(true) defer tx.Abort() - if err := s.aclBindingRuleDeleteTxn(tx, idx, value, index); err != nil { + if err := s.aclBindingRuleDeleteTxn(tx, idx, id, entMeta); err != nil { return err } - if err := indexUpdateMaxTxn(tx, idx, "acl-binding-rules"); err != nil { - return fmt.Errorf("failed updating index: %v", err) - } tx.Commit() return nil } -func (s *Store) aclBindingRuleDeleteTxn(tx *memdb.Txn, idx uint64, value, index string) error { +func (s *Store) aclBindingRuleDeleteTxn(tx *memdb.Txn, idx uint64, id string, entMeta *structs.EnterpriseMeta) error { // Look up the existing binding rule - rawRule, err := tx.First("acl-binding-rules", index, value) + _, rawRule, err := s.aclBindingRuleGetByID(tx, id, entMeta) if err != nil { return fmt.Errorf("failed acl binding rule lookup: %v", err) } @@ -1960,15 +1685,15 @@ func (s *Store) aclBindingRuleDeleteTxn(tx *memdb.Txn, idx uint64, value, index rule := rawRule.(*structs.ACLBindingRule) - if err := tx.Delete("acl-binding-rules", rule); err != nil { + if err := s.aclBindingRuleDeleteWithRule(tx, rule, idx); err != nil { return fmt.Errorf("failed deleting acl binding rule: %v", err) } return nil } -func (s *Store) aclBindingRuleDeleteAllForAuthMethodTxn(tx *memdb.Txn, idx uint64, methodName string) error { +func (s *Store) aclBindingRuleDeleteAllForAuthMethodTxn(tx *memdb.Txn, idx uint64, methodName string, entMeta *structs.EnterpriseMeta) error { // collect them all - iter, err := tx.Get("acl-binding-rules", "authmethod", methodName) + iter, err := s.aclBindingRuleListByAuthMethod(tx, methodName, entMeta) if err != nil { return fmt.Errorf("failed acl binding rule lookup: %v", err) } @@ -1982,14 +1707,10 @@ func (s *Store) aclBindingRuleDeleteAllForAuthMethodTxn(tx *memdb.Txn, idx uint6 if len(rules) > 0 { // delete them all for _, rule := range rules { - if err := tx.Delete("acl-binding-rules", rule); err != nil { - return fmt.Errorf("failed deleting acl binding rule: %v", err) + if err := s.aclBindingRuleDeleteWithRule(tx, rule, idx); err != nil { + return err } } - - if err := indexUpdateMaxTxn(tx, idx, "acl-binding-rules"); err != nil { - return fmt.Errorf("failed updating index: %v", err) - } } return nil @@ -2006,11 +1727,6 @@ func (s *Store) ACLAuthMethodBatchSet(idx uint64, methods structs.ACLAuthMethods return err } } - - if err := indexUpdateMaxTxn(tx, idx, "acl-auth-methods"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } - tx.Commit() return nil } @@ -2022,9 +1738,6 @@ func (s *Store) ACLAuthMethodSet(idx uint64, method *structs.ACLAuthMethod) erro if err := s.aclAuthMethodSetTxn(tx, idx, method); err != nil { return err } - if err := indexUpdateMaxTxn(tx, idx, "acl-auth-methods"); err != nil { - return fmt.Errorf("failed updating index: %s", err) - } tx.Commit() return nil @@ -2038,46 +1751,49 @@ func (s *Store) aclAuthMethodSetTxn(tx *memdb.Txn, idx uint64, method *structs.A return ErrMissingACLAuthMethodType } - existing, err := tx.First("acl-auth-methods", "id", method.Name) + var existing *structs.ACLAuthMethod + _, existingRaw, err := s.aclAuthMethodGetByName(tx, method.Name, &method.EnterpriseMeta) if err != nil { return fmt.Errorf("failed acl auth method lookup: %v", err) } + if err := s.aclAuthMethodUpsertValidateEnterprise(tx, method, existing); err != nil { + return err + } + // Set the indexes - if existing != nil { - method.CreateIndex = existing.(*structs.ACLAuthMethod).CreateIndex + if existingRaw != nil { + existing = existingRaw.(*structs.ACLAuthMethod) + method.CreateIndex = existing.CreateIndex method.ModifyIndex = idx } else { method.CreateIndex = idx method.ModifyIndex = idx } - if err := tx.Insert("acl-auth-methods", method); err != nil { - return fmt.Errorf("failed inserting acl auth method: %v", err) - } - return nil + return s.aclAuthMethodInsert(tx, method, true) } -func (s *Store) ACLAuthMethodGetByName(ws memdb.WatchSet, name string) (uint64, *structs.ACLAuthMethod, error) { - return s.aclAuthMethodGet(ws, name, "id") +func (s *Store) ACLAuthMethodGetByName(ws memdb.WatchSet, name string, entMeta *structs.EnterpriseMeta) (uint64, *structs.ACLAuthMethod, error) { + return s.aclAuthMethodGet(ws, name, entMeta) } -func (s *Store) aclAuthMethodGet(ws memdb.WatchSet, value, index string) (uint64, *structs.ACLAuthMethod, error) { +func (s *Store) aclAuthMethodGet(ws memdb.WatchSet, name string, entMeta *structs.EnterpriseMeta) (uint64, *structs.ACLAuthMethod, error) { tx := s.db.Txn(false) defer tx.Abort() - method, err := s.getAuthMethodWithTxn(tx, ws, value, index) + method, err := s.getAuthMethodWithTxn(tx, ws, name, entMeta) if err != nil { return 0, nil, err } - idx := maxIndexTxn(tx, "acl-auth-methods") + idx := s.aclAuthMethodMaxIndex(tx, method, entMeta) return idx, method, nil } -func (s *Store) getAuthMethodWithTxn(tx *memdb.Txn, ws memdb.WatchSet, value, index string) (*structs.ACLAuthMethod, error) { - watchCh, rawMethod, err := tx.FirstWatch("acl-auth-methods", index, value) +func (s *Store) getAuthMethodWithTxn(tx *memdb.Txn, ws memdb.WatchSet, name string, entMeta *structs.EnterpriseMeta) (*structs.ACLAuthMethod, error) { + watchCh, rawMethod, err := s.aclAuthMethodGetByName(tx, name, entMeta) if err != nil { return nil, fmt.Errorf("failed acl auth method lookup: %v", err) } @@ -2090,11 +1806,11 @@ func (s *Store) getAuthMethodWithTxn(tx *memdb.Txn, ws memdb.WatchSet, value, in return nil, nil } -func (s *Store) ACLAuthMethodList(ws memdb.WatchSet) (uint64, structs.ACLAuthMethods, error) { +func (s *Store) ACLAuthMethodList(ws memdb.WatchSet, entMeta *structs.EnterpriseMeta) (uint64, structs.ACLAuthMethods, error) { tx := s.db.Txn(false) defer tx.Abort() - iter, err := tx.Get("acl-auth-methods", "id") + iter, err := s.aclAuthMethodList(tx, entMeta) if err != nil { return 0, nil, fmt.Errorf("failed acl auth method lookup: %v", err) } @@ -2107,48 +1823,46 @@ func (s *Store) ACLAuthMethodList(ws memdb.WatchSet) (uint64, structs.ACLAuthMet } // Get the table index. - idx := maxIndexTxn(tx, "acl-auth-methods") + idx := s.aclAuthMethodMaxIndex(tx, nil, entMeta) return idx, result, nil } -func (s *Store) ACLAuthMethodDeleteByName(idx uint64, name string) error { - return s.aclAuthMethodDelete(idx, name, "id") +func (s *Store) ACLAuthMethodDeleteByName(idx uint64, name string, entMeta *structs.EnterpriseMeta) error { + return s.aclAuthMethodDelete(idx, name, entMeta) } -func (s *Store) ACLAuthMethodBatchDelete(idx uint64, names []string) error { +func (s *Store) ACLAuthMethodBatchDelete(idx uint64, names []string, entMeta *structs.EnterpriseMeta) error { tx := s.db.Txn(true) defer tx.Abort() for _, name := range names { - s.aclAuthMethodDeleteTxn(tx, idx, name, "id") + // NOTE: it may seem odd that one EnterpriseMeta is being used for all of the auth methods being + // deleted. However we never actually batch these deletions as auth methods are not replicated + // Therefore this is fine but if we ever change that precondition then this will be wrong (unless + // we ensure all deletions in a batch should have the same enterprise meta) + s.aclAuthMethodDeleteTxn(tx, idx, name, entMeta) } - if err := indexUpdateMaxTxn(tx, idx, "acl-auth-methods"); err != nil { - return fmt.Errorf("failed updating index: %v", err) - } tx.Commit() return nil } -func (s *Store) aclAuthMethodDelete(idx uint64, value, index string) error { +func (s *Store) aclAuthMethodDelete(idx uint64, name string, entMeta *structs.EnterpriseMeta) error { tx := s.db.Txn(true) defer tx.Abort() - if err := s.aclAuthMethodDeleteTxn(tx, idx, value, index); err != nil { + if err := s.aclAuthMethodDeleteTxn(tx, idx, name, entMeta); err != nil { return err } - if err := indexUpdateMaxTxn(tx, idx, "acl-auth-methods"); err != nil { - return fmt.Errorf("failed updating index: %v", err) - } tx.Commit() return nil } -func (s *Store) aclAuthMethodDeleteTxn(tx *memdb.Txn, idx uint64, value, index string) error { +func (s *Store) aclAuthMethodDeleteTxn(tx *memdb.Txn, idx uint64, name string, entMeta *structs.EnterpriseMeta) error { // Look up the existing method - rawMethod, err := tx.First("acl-auth-methods", index, value) + _, rawMethod, err := s.aclAuthMethodGetByName(tx, name, entMeta) if err != nil { return fmt.Errorf("failed acl auth method lookup: %v", err) } @@ -2159,16 +1873,13 @@ func (s *Store) aclAuthMethodDeleteTxn(tx *memdb.Txn, idx uint64, value, index s method := rawMethod.(*structs.ACLAuthMethod) - if err := s.aclBindingRuleDeleteAllForAuthMethodTxn(tx, idx, method.Name); err != nil { + if err := s.aclBindingRuleDeleteAllForAuthMethodTxn(tx, idx, method.Name, entMeta); err != nil { return err } - if err := s.aclTokenDeleteAllForAuthMethodTxn(tx, idx, method.Name); err != nil { + if err := s.aclTokenDeleteAllForAuthMethodTxn(tx, idx, method.Name, entMeta); err != nil { return err } - if err := tx.Delete("acl-auth-methods", method); err != nil { - return fmt.Errorf("failed deleting acl auth method: %v", err) - } - return nil + return s.aclAuthMethodDeleteWithMethod(tx, method, idx) } diff --git a/agent/consul/state/acl_oss.go b/agent/consul/state/acl_oss.go new file mode 100644 index 0000000000..fef5ba6edc --- /dev/null +++ b/agent/consul/state/acl_oss.go @@ -0,0 +1,501 @@ +// +build !consulent + +package state + +import ( + "fmt" + + "github.com/hashicorp/consul/agent/structs" + memdb "github.com/hashicorp/go-memdb" +) + +/////////////////////////////////////////////////////////////////////////////// +///// ACL Table Schemas ///// +/////////////////////////////////////////////////////////////////////////////// + +func tokensTableSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: "acl-tokens", + Indexes: map[string]*memdb.IndexSchema{ + "accessor": &memdb.IndexSchema{ + Name: "accessor", + // DEPRECATED (ACL-Legacy-Compat) - we should not AllowMissing here once legacy compat is removed + AllowMissing: true, + Unique: true, + Indexer: &memdb.UUIDFieldIndex{ + Field: "AccessorID", + }, + }, + "id": &memdb.IndexSchema{ + Name: "id", + AllowMissing: false, + Unique: true, + Indexer: &memdb.StringFieldIndex{ + Field: "SecretID", + Lowercase: false, + }, + }, + "policies": &memdb.IndexSchema{ + Name: "policies", + // Need to allow missing for the anonymous token + AllowMissing: true, + Unique: false, + Indexer: &TokenPoliciesIndex{}, + }, + "roles": &memdb.IndexSchema{ + Name: "roles", + AllowMissing: true, + Unique: false, + Indexer: &TokenRolesIndex{}, + }, + "authmethod": &memdb.IndexSchema{ + Name: "authmethod", + AllowMissing: true, + Unique: false, + Indexer: &memdb.StringFieldIndex{ + Field: "AuthMethod", + Lowercase: false, + }, + }, + "local": &memdb.IndexSchema{ + Name: "local", + AllowMissing: false, + Unique: false, + Indexer: &memdb.ConditionalIndex{ + Conditional: func(obj interface{}) (bool, error) { + if token, ok := obj.(*structs.ACLToken); ok { + return token.Local, nil + } + return false, nil + }, + }, + }, + "expires-global": { + Name: "expires-global", + AllowMissing: true, + Unique: false, + Indexer: &TokenExpirationIndex{LocalFilter: false}, + }, + "expires-local": { + Name: "expires-local", + AllowMissing: true, + Unique: false, + Indexer: &TokenExpirationIndex{LocalFilter: true}, + }, + + //DEPRECATED (ACL-Legacy-Compat) - This index is only needed while we support upgrading v1 to v2 acls + // This table indexes all the ACL tokens that do not have an AccessorID + "needs-upgrade": &memdb.IndexSchema{ + Name: "needs-upgrade", + AllowMissing: false, + Unique: false, + Indexer: &memdb.ConditionalIndex{ + Conditional: func(obj interface{}) (bool, error) { + if token, ok := obj.(*structs.ACLToken); ok { + return token.AccessorID == "", nil + } + return false, nil + }, + }, + }, + }, + } +} + +func policiesTableSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: "acl-policies", + Indexes: map[string]*memdb.IndexSchema{ + "id": &memdb.IndexSchema{ + Name: "id", + AllowMissing: false, + Unique: true, + Indexer: &memdb.UUIDFieldIndex{ + Field: "ID", + }, + }, + "name": &memdb.IndexSchema{ + Name: "name", + AllowMissing: false, + Unique: true, + Indexer: &memdb.StringFieldIndex{ + Field: "Name", + // TODO (ACL-V2) - should we coerce to lowercase? + Lowercase: true, + }, + }, + }, + } +} + +func rolesTableSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: "acl-roles", + Indexes: map[string]*memdb.IndexSchema{ + "id": &memdb.IndexSchema{ + Name: "id", + AllowMissing: false, + Unique: true, + Indexer: &memdb.UUIDFieldIndex{ + Field: "ID", + }, + }, + "name": &memdb.IndexSchema{ + Name: "name", + AllowMissing: false, + Unique: true, + Indexer: &memdb.StringFieldIndex{ + Field: "Name", + Lowercase: true, + }, + }, + "policies": &memdb.IndexSchema{ + Name: "policies", + // Need to allow missing for the anonymous token + AllowMissing: true, + Unique: false, + Indexer: &RolePoliciesIndex{}, + }, + }, + } +} + +func bindingRulesTableSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: "acl-binding-rules", + Indexes: map[string]*memdb.IndexSchema{ + "id": &memdb.IndexSchema{ + Name: "id", + AllowMissing: false, + Unique: true, + Indexer: &memdb.UUIDFieldIndex{ + Field: "ID", + }, + }, + "authmethod": &memdb.IndexSchema{ + Name: "authmethod", + AllowMissing: false, + Unique: false, + Indexer: &memdb.StringFieldIndex{ + Field: "AuthMethod", + Lowercase: true, + }, + }, + }, + } +} + +func authMethodsTableSchema() *memdb.TableSchema { + return &memdb.TableSchema{ + Name: "acl-auth-methods", + Indexes: map[string]*memdb.IndexSchema{ + "id": &memdb.IndexSchema{ + Name: "id", + AllowMissing: false, + Unique: true, + Indexer: &memdb.StringFieldIndex{ + Field: "Name", + Lowercase: true, + }, + }, + }, + } +} + +/////////////////////////////////////////////////////////////////////////////// +///// ACL Policy Functions ///// +/////////////////////////////////////////////////////////////////////////////// + +func (s *Store) aclPolicyInsert(tx *memdb.Txn, policy *structs.ACLPolicy, updateIndexes bool) error { + if err := tx.Insert("acl-policies", policy); err != nil { + return fmt.Errorf("failed inserting acl policy: %v", err) + } + + if updateIndexes { + if err := tx.Insert("index", &IndexEntry{"acl-policies", policy.ModifyIndex}); err != nil { + return fmt.Errorf("failed updating acl policies index: %v", err) + } + } + + return nil +} + +func (s *Store) aclPolicyGetByID(tx *memdb.Txn, id string, _ *structs.EnterpriseMeta) (<-chan struct{}, interface{}, error) { + return tx.FirstWatch("acl-policies", "id", id) +} + +func (s *Store) aclPolicyGetByName(tx *memdb.Txn, name string, _ *structs.EnterpriseMeta) (<-chan struct{}, interface{}, error) { + return tx.FirstWatch("acl-policies", "name", name) +} + +func (s *Store) aclPolicyList(tx *memdb.Txn, _ *structs.EnterpriseMeta) (memdb.ResultIterator, error) { + return tx.Get("acl-policies", "id") +} + +func (s *Store) aclPolicyDeleteWithPolicy(tx *memdb.Txn, policy *structs.ACLPolicy, idx uint64) error { + // remove the policy + if err := tx.Delete("acl-policies", policy); err != nil { + return fmt.Errorf("failed deleting acl policy: %v", err) + } + + // update the overall acl-policies index + if err := tx.Insert("index", &IndexEntry{"acl-policies", idx}); err != nil { + return fmt.Errorf("failed updating acl policies index: %v", err) + } + return nil +} + +func (s *Store) aclPolicyMaxIndex(tx *memdb.Txn, _ *structs.ACLPolicy, _ *structs.EnterpriseMeta) uint64 { + return maxIndexTxn(tx, "acl-policies") +} + +func (s *Store) aclPolicyUpsertValidateEnterprise(*memdb.Txn, *structs.ACLPolicy, *structs.ACLPolicy) error { + return nil +} + +func (s *Store) ACLPolicyUpsertValidateEnterprise(*structs.ACLPolicy, *structs.ACLPolicy) error { + return nil +} + +/////////////////////////////////////////////////////////////////////////////// +///// ACL Token Functions ///// +/////////////////////////////////////////////////////////////////////////////// + +func (s *Store) aclTokenInsert(tx *memdb.Txn, token *structs.ACLToken, updateIndexes bool) error { + // insert the token into memdb + if err := tx.Insert("acl-tokens", token); err != nil { + return fmt.Errorf("failed inserting acl token: %v", err) + } + + if updateIndexes { + // update the overall acl-tokens index + if err := tx.Insert("index", &IndexEntry{"acl-tokens", token.ModifyIndex}); err != nil { + return fmt.Errorf("failed updating acl tokens index: %v", err) + } + } + + return nil +} + +func (s *Store) aclTokenGetFromIndex(tx *memdb.Txn, id string, index string, entMeta *structs.EnterpriseMeta) (<-chan struct{}, interface{}, error) { + return tx.FirstWatch("acl-tokens", index, id) +} + +func (s *Store) aclTokenListAll(tx *memdb.Txn, _ *structs.EnterpriseMeta) (memdb.ResultIterator, error) { + return tx.Get("acl-tokens", "id") +} + +func (s *Store) aclTokenListLocal(tx *memdb.Txn, _ *structs.EnterpriseMeta) (memdb.ResultIterator, error) { + return tx.Get("acl-tokens", "local", true) +} + +func (s *Store) aclTokenListGlobal(tx *memdb.Txn, _ *structs.EnterpriseMeta) (memdb.ResultIterator, error) { + return tx.Get("acl-tokens", "local", false) +} + +func (s *Store) aclTokenListByPolicy(tx *memdb.Txn, policy string, _ *structs.EnterpriseMeta) (memdb.ResultIterator, error) { + return tx.Get("acl-tokens", "policies", policy) +} + +func (s *Store) aclTokenListByRole(tx *memdb.Txn, role string, _ *structs.EnterpriseMeta) (memdb.ResultIterator, error) { + return tx.Get("acl-tokens", "roles", role) +} + +func (s *Store) aclTokenListByAuthMethod(tx *memdb.Txn, authMethod string, _ *structs.EnterpriseMeta) (memdb.ResultIterator, error) { + return tx.Get("acl-tokens", "authmethod", authMethod) +} + +func (s *Store) aclTokenDeleteWithToken(tx *memdb.Txn, token *structs.ACLToken, idx uint64) error { + // remove the token + if err := tx.Delete("acl-tokens", token); err != nil { + return fmt.Errorf("failed deleting acl token: %v", err) + } + + // update the overall acl-tokens index + if err := tx.Insert("index", &IndexEntry{"acl-tokens", idx}); err != nil { + return fmt.Errorf("failed updating acl tokens index: %v", err) + } + return nil +} + +func (s *Store) aclTokenMaxIndex(tx *memdb.Txn, _ *structs.ACLToken, entMeta *structs.EnterpriseMeta) uint64 { + return maxIndexTxn(tx, "acl-tokens") +} + +func (s *Store) aclTokenUpsertValidateEnterprise(tx *memdb.Txn, token *structs.ACLToken, existing *structs.ACLToken) error { + return nil +} + +func (s *Store) ACLTokenUpsertValidateEnterprise(token *structs.ACLToken, existing *structs.ACLToken) error { + return nil +} + +/////////////////////////////////////////////////////////////////////////////// +///// ACL Role Functions ///// +/////////////////////////////////////////////////////////////////////////////// + +func (s *Store) aclRoleInsert(tx *memdb.Txn, role *structs.ACLRole, updateIndexes bool) error { + // insert the role into memdb + if err := tx.Insert("acl-roles", role); err != nil { + return fmt.Errorf("failed inserting acl role: %v", err) + } + + if updateIndexes { + // update the overall acl-roles index + if err := tx.Insert("index", &IndexEntry{"acl-roles", role.ModifyIndex}); err != nil { + return fmt.Errorf("failed updating acl roles index: %v", err) + } + } + return nil +} + +func (s *Store) aclRoleGetByID(tx *memdb.Txn, id string, _ *structs.EnterpriseMeta) (<-chan struct{}, interface{}, error) { + return tx.FirstWatch("acl-roles", "id", id) +} + +func (s *Store) aclRoleGetByName(tx *memdb.Txn, name string, _ *structs.EnterpriseMeta) (<-chan struct{}, interface{}, error) { + return tx.FirstWatch("acl-roles", "name", name) +} + +func (s *Store) aclRoleList(tx *memdb.Txn, _ *structs.EnterpriseMeta) (memdb.ResultIterator, error) { + return tx.Get("acl-roles", "id") +} + +func (s *Store) aclRoleListByPolicy(tx *memdb.Txn, policy string, _ *structs.EnterpriseMeta) (memdb.ResultIterator, error) { + return tx.Get("acl-roles", "policies", policy) +} + +func (s *Store) aclRoleDeleteWithRole(tx *memdb.Txn, role *structs.ACLRole, idx uint64) error { + // remove the role + if err := tx.Delete("acl-roles", role); err != nil { + return fmt.Errorf("failed deleting acl role: %v", err) + } + + // update the overall acl-roles index + if err := tx.Insert("index", &IndexEntry{"acl-roles", idx}); err != nil { + return fmt.Errorf("failed updating acl policies index: %v", err) + } + return nil +} + +func (s *Store) aclRoleMaxIndex(tx *memdb.Txn, _ *structs.ACLRole, _ *structs.EnterpriseMeta) uint64 { + return maxIndexTxn(tx, "acl-roles") +} + +func (s *Store) aclRoleUpsertValidateEnterprise(tx *memdb.Txn, role *structs.ACLRole, existing *structs.ACLRole) error { + return nil +} + +func (s *Store) ACLRoleUpsertValidateEnterprise(role *structs.ACLRole, existing *structs.ACLRole) error { + return nil +} + +/////////////////////////////////////////////////////////////////////////////// +///// ACL Binding Rule Functions ///// +/////////////////////////////////////////////////////////////////////////////// + +func (s *Store) aclBindingRuleInsert(tx *memdb.Txn, rule *structs.ACLBindingRule, updateIndexes bool) error { + // insert the role into memdb + if err := tx.Insert("acl-binding-rules", rule); err != nil { + return fmt.Errorf("failed inserting acl role: %v", err) + } + + if updateIndexes { + // update the overall acl-binding-rules index + if err := tx.Insert("index", &IndexEntry{"acl-binding-rules", rule.ModifyIndex}); err != nil { + return fmt.Errorf("failed updating acl binding-rules index: %v", err) + } + } + + return nil +} + +func (s *Store) aclBindingRuleGetByID(tx *memdb.Txn, id string, _ *structs.EnterpriseMeta) (<-chan struct{}, interface{}, error) { + return tx.FirstWatch("acl-binding-rules", "id", id) +} + +func (s *Store) aclBindingRuleList(tx *memdb.Txn, _ *structs.EnterpriseMeta) (memdb.ResultIterator, error) { + return tx.Get("acl-binding-rules", "id") +} + +func (s *Store) aclBindingRuleListByAuthMethod(tx *memdb.Txn, method string, _ *structs.EnterpriseMeta) (memdb.ResultIterator, error) { + return tx.Get("acl-binding-rules", "authmethod", method) +} + +func (s *Store) aclBindingRuleDeleteWithRule(tx *memdb.Txn, rule *structs.ACLBindingRule, idx uint64) error { + // remove the rule + if err := tx.Delete("acl-binding-rules", rule); err != nil { + return fmt.Errorf("failed deleting acl binding rule: %v", err) + } + + // update the overall acl-binding-rules index + if err := tx.Insert("index", &IndexEntry{"acl-binding-rules", idx}); err != nil { + return fmt.Errorf("failed updating acl binding rules index: %v", err) + } + return nil +} + +func (s *Store) aclBindingRuleMaxIndex(tx *memdb.Txn, _ *structs.ACLBindingRule, entMeta *structs.EnterpriseMeta) uint64 { + return maxIndexTxn(tx, "acl-binding-rules") +} + +func (s *Store) aclBindingRuleUpsertValidateEnterprise(tx *memdb.Txn, rule *structs.ACLBindingRule, existing *structs.ACLBindingRule) error { + return nil +} + +func (s *Store) ACLBindingRuleUpsertValidateEnterprise(rule *structs.ACLBindingRule, existing *structs.ACLBindingRule) error { + return nil +} + +/////////////////////////////////////////////////////////////////////////////// +///// ACL Auth Method Functions ///// +/////////////////////////////////////////////////////////////////////////////// + +func (s *Store) aclAuthMethodInsert(tx *memdb.Txn, method *structs.ACLAuthMethod, updateIndexes bool) error { + // insert the role into memdb + if err := tx.Insert("acl-auth-methods", method); err != nil { + return fmt.Errorf("failed inserting acl role: %v", err) + } + + if updateIndexes { + // update the overall acl-auth-methods index + if err := tx.Insert("index", &IndexEntry{"acl-auth-methods", method.ModifyIndex}); err != nil { + return fmt.Errorf("failed updating acl auth methods index: %v", err) + } + } + + return nil +} + +func (s *Store) aclAuthMethodGetByName(tx *memdb.Txn, method string, _ *structs.EnterpriseMeta) (<-chan struct{}, interface{}, error) { + return tx.FirstWatch("acl-auth-methods", "id", method) +} + +func (s *Store) aclAuthMethodList(tx *memdb.Txn, entMeta *structs.EnterpriseMeta) (memdb.ResultIterator, error) { + return tx.Get("acl-auth-methods", "id") +} + +func (s *Store) aclAuthMethodDeleteWithMethod(tx *memdb.Txn, method *structs.ACLAuthMethod, idx uint64) error { + // remove the method + if err := tx.Delete("acl-auth-methods", method); err != nil { + return fmt.Errorf("failed deleting acl auth method: %v", err) + } + + // update the overall acl-auth-methods index + if err := tx.Insert("index", &IndexEntry{"acl-auth-methods", idx}); err != nil { + return fmt.Errorf("failed updating acl auth methods index: %v", err) + } + return nil +} + +func (s *Store) aclAuthMethodMaxIndex(tx *memdb.Txn, _ *structs.ACLAuthMethod, entMeta *structs.EnterpriseMeta) uint64 { + return maxIndexTxn(tx, "acl-auth-methods") +} + +func (s *Store) aclAuthMethodUpsertValidateEnterprise(tx *memdb.Txn, method *structs.ACLAuthMethod, existing *structs.ACLAuthMethod) error { + return nil +} + +func (s *Store) ACLAuthMethodUpsertValidateEnterprise(method *structs.ACLAuthMethod, existing *structs.ACLAuthMethod) error { + return nil +} diff --git a/agent/consul/state/acl_test.go b/agent/consul/state/acl_test.go index 6057b337dc..62b2c28599 100644 --- a/agent/consul/state/acl_test.go +++ b/agent/consul/state/acl_test.go @@ -218,7 +218,7 @@ func TestStateStore_ACLBootstrap(t *testing.T) { require.Equal(t, uint64(3), index) // Make sure the ACLs are in an expected state. - _, tokens, err := s.ACLTokenList(nil, true, true, "", "", "") + _, tokens, err := s.ACLTokenList(nil, true, true, "", "", "", nil) require.NoError(t, err) require.Len(t, tokens, 1) compareTokens(t, token1, tokens[0]) @@ -232,7 +232,7 @@ func TestStateStore_ACLBootstrap(t *testing.T) { err = s.ACLBootstrap(32, index, token2.Clone(), false) require.NoError(t, err) - _, tokens, err = s.ACLTokenList(nil, true, true, "", "", "") + _, tokens, err = s.ACLTokenList(nil, true, true, "", "", "", nil) require.NoError(t, err) require.Len(t, tokens, 2) } @@ -286,7 +286,7 @@ func TestStateStore_ACLToken_SetGet_Legacy(t *testing.T) { require.NoError(t, s.ACLTokenSet(2, token.Clone(), true)) - idx, rtoken, err := s.ACLTokenGetBySecret(nil, token.SecretID) + idx, rtoken, err := s.ACLTokenGetBySecret(nil, token.SecretID, nil) require.NoError(t, err) require.Equal(t, uint64(2), idx) require.NotNil(t, rtoken) @@ -319,7 +319,7 @@ func TestStateStore_ACLToken_SetGet_Legacy(t *testing.T) { require.NoError(t, s.ACLTokenSet(3, update.Clone(), true)) - idx, rtoken, err := s.ACLTokenGetBySecret(nil, original.SecretID) + idx, rtoken, err := s.ACLTokenGetBySecret(nil, original.SecretID, nil) require.NoError(t, err) require.Equal(t, uint64(3), idx) require.NotNil(t, rtoken) @@ -498,7 +498,7 @@ func TestStateStore_ACLToken_SetGet(t *testing.T) { require.NoError(t, s.ACLTokenSet(2, token.Clone(), false)) - idx, rtoken, err := s.ACLTokenGetByAccessor(nil, "daf37c07-d04d-4fd5-9678-a8206a57d61a") + idx, rtoken, err := s.ACLTokenGetByAccessor(nil, "daf37c07-d04d-4fd5-9678-a8206a57d61a", nil) require.NoError(t, err) require.Equal(t, uint64(2), idx) compareTokens(t, token, rtoken) @@ -554,7 +554,7 @@ func TestStateStore_ACLToken_SetGet(t *testing.T) { require.NoError(t, s.ACLTokenSet(3, updated.Clone(), false)) - idx, rtoken, err := s.ACLTokenGetByAccessor(nil, "daf37c07-d04d-4fd5-9678-a8206a57d61a") + idx, rtoken, err := s.ACLTokenGetByAccessor(nil, "daf37c07-d04d-4fd5-9678-a8206a57d61a", nil) require.NoError(t, err) require.Equal(t, uint64(3), idx) compareTokens(t, updated, rtoken) @@ -588,7 +588,7 @@ func TestStateStore_ACLToken_SetGet(t *testing.T) { require.NoError(t, s.ACLTokenSet(2, token.Clone(), false)) - idx, rtoken, err := s.ACLTokenGetByAccessor(nil, "daf37c07-d04d-4fd5-9678-a8206a57d61a") + idx, rtoken, err := s.ACLTokenGetByAccessor(nil, "daf37c07-d04d-4fd5-9678-a8206a57d61a", nil) require.NoError(t, err) require.Equal(t, uint64(2), idx) compareTokens(t, token, rtoken) @@ -622,7 +622,7 @@ func TestStateStore_ACLTokens_UpsertBatchRead(t *testing.T) { require.NoError(t, s.ACLTokenBatchSet(2, tokens, true, false, false)) - _, token, err := s.ACLTokenGetByAccessor(nil, tokens[0].AccessorID) + _, token, err := s.ACLTokenGetByAccessor(nil, tokens[0].AccessorID, nil) require.NoError(t, err) require.Nil(t, token) }) @@ -654,7 +654,7 @@ func TestStateStore_ACLTokens_UpsertBatchRead(t *testing.T) { require.NoError(t, s.ACLTokenBatchSet(6, updated, true, false, false)) - _, token, err := s.ACLTokenGetByAccessor(nil, tokens[0].AccessorID) + _, token, err := s.ACLTokenGetByAccessor(nil, tokens[0].AccessorID, nil) require.NoError(t, err) require.NotNil(t, token) require.Equal(t, "", token.Description) @@ -683,7 +683,7 @@ func TestStateStore_ACLTokens_UpsertBatchRead(t *testing.T) { require.NoError(t, s.ACLTokenBatchSet(6, updated, true, false, false)) - _, token, err := s.ACLTokenGetByAccessor(nil, tokens[0].AccessorID) + _, token, err := s.ACLTokenGetByAccessor(nil, tokens[0].AccessorID, nil) require.NoError(t, err) require.NotNil(t, token) require.Equal(t, "", token.Description) @@ -1203,7 +1203,7 @@ func TestStateStore_ACLToken_List(t *testing.T) { {testPolicyID_A, testRoleID_A, ""}, } { t.Run(fmt.Sprintf("can't filter on more than one: %s/%s/%s", tc.policy, tc.role, tc.methodName), func(t *testing.T) { - _, _, err := s.ACLTokenList(nil, false, false, tc.policy, tc.role, tc.methodName) + _, _, err := s.ACLTokenList(nil, false, false, tc.policy, tc.role, tc.methodName, nil) require.Error(t, err) }) } @@ -1212,7 +1212,7 @@ func TestStateStore_ACLToken_List(t *testing.T) { tc := tc // capture range variable t.Run(tc.name, func(t *testing.T) { t.Parallel() - _, tokens, err := s.ACLTokenList(nil, tc.local, tc.global, tc.policy, tc.role, tc.methodName) + _, tokens, err := s.ACLTokenList(nil, tc.local, tc.global, tc.policy, tc.role, tc.methodName, nil) require.NoError(t, err) require.Len(t, tokens, len(tc.accessors)) tokens.Sort() @@ -1246,7 +1246,7 @@ func TestStateStore_ACLToken_FixupPolicyLinks(t *testing.T) { require.NoError(t, s.ACLTokenSet(2, token, false)) - _, retrieved, err := s.ACLTokenGetByAccessor(nil, token.AccessorID) + _, retrieved, err := s.ACLTokenGetByAccessor(nil, token.AccessorID, nil) require.NoError(t, err) // pointer equality check these should be identical require.True(t, token == retrieved) @@ -1265,7 +1265,7 @@ func TestStateStore_ACLToken_FixupPolicyLinks(t *testing.T) { require.NoError(t, s.ACLPolicySet(3, renamed)) // retrieve the token again - _, retrieved, err = s.ACLTokenGetByAccessor(nil, token.AccessorID) + _, retrieved, err = s.ACLTokenGetByAccessor(nil, token.AccessorID, nil) require.NoError(t, err) // pointer equality check these should be different if we cloned things appropriately require.True(t, token != retrieved) @@ -1273,7 +1273,7 @@ func TestStateStore_ACLToken_FixupPolicyLinks(t *testing.T) { require.Equal(t, "node-read-renamed", retrieved.Policies[0].Name) // list tokens without stale links - _, tokens, err := s.ACLTokenList(nil, true, true, "", "", "") + _, tokens, err := s.ACLTokenList(nil, true, true, "", "", "", nil) require.NoError(t, err) found := false @@ -1307,17 +1307,17 @@ func TestStateStore_ACLToken_FixupPolicyLinks(t *testing.T) { require.True(t, found) // delete the policy - require.NoError(t, s.ACLPolicyDeleteByID(4, testPolicyID_A)) + require.NoError(t, s.ACLPolicyDeleteByID(4, testPolicyID_A, nil)) // retrieve the token again - _, retrieved, err = s.ACLTokenGetByAccessor(nil, token.AccessorID) + _, retrieved, err = s.ACLTokenGetByAccessor(nil, token.AccessorID, nil) require.NoError(t, err) // pointer equality check these should be different if we cloned things appropriately require.True(t, token != retrieved) require.Len(t, retrieved.Policies, 0) // list tokens without stale links - _, tokens, err = s.ACLTokenList(nil, true, true, "", "", "") + _, tokens, err = s.ACLTokenList(nil, true, true, "", "", "", nil) require.NoError(t, err) found = false @@ -1372,7 +1372,7 @@ func TestStateStore_ACLToken_FixupRoleLinks(t *testing.T) { require.NoError(t, s.ACLTokenSet(2, token, false)) - _, retrieved, err := s.ACLTokenGetByAccessor(nil, token.AccessorID) + _, retrieved, err := s.ACLTokenGetByAccessor(nil, token.AccessorID, nil) require.NoError(t, err) // pointer equality check these should be identical require.True(t, token == retrieved) @@ -1394,7 +1394,7 @@ func TestStateStore_ACLToken_FixupRoleLinks(t *testing.T) { require.NoError(t, s.ACLRoleSet(3, renamed)) // retrieve the token again - _, retrieved, err = s.ACLTokenGetByAccessor(nil, token.AccessorID) + _, retrieved, err = s.ACLTokenGetByAccessor(nil, token.AccessorID, nil) require.NoError(t, err) // pointer equality check these should be different if we cloned things appropriately require.True(t, token != retrieved) @@ -1402,7 +1402,7 @@ func TestStateStore_ACLToken_FixupRoleLinks(t *testing.T) { require.Equal(t, "node-read-role-renamed", retrieved.Roles[0].Name) // list tokens without stale links - _, tokens, err := s.ACLTokenList(nil, true, true, "", "", "") + _, tokens, err := s.ACLTokenList(nil, true, true, "", "", "", nil) require.NoError(t, err) found := false @@ -1436,17 +1436,17 @@ func TestStateStore_ACLToken_FixupRoleLinks(t *testing.T) { require.True(t, found) // delete the role - require.NoError(t, s.ACLRoleDeleteByID(4, testRoleID_A)) + require.NoError(t, s.ACLRoleDeleteByID(4, testRoleID_A, nil)) // retrieve the token again - _, retrieved, err = s.ACLTokenGetByAccessor(nil, token.AccessorID) + _, retrieved, err = s.ACLTokenGetByAccessor(nil, token.AccessorID, nil) require.NoError(t, err) // pointer equality check these should be different if we cloned things appropriately require.True(t, token != retrieved) require.Len(t, retrieved.Roles, 0) // list tokens without stale links - _, tokens, err = s.ACLTokenList(nil, true, true, "", "", "") + _, tokens, err = s.ACLTokenList(nil, true, true, "", "", "", nil) require.NoError(t, err) found = false @@ -1498,13 +1498,13 @@ func TestStateStore_ACLToken_Delete(t *testing.T) { require.NoError(t, s.ACLTokenSet(2, token.Clone(), false)) - _, rtoken, err := s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b") + _, rtoken, err := s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b", nil) require.NoError(t, err) require.NotNil(t, rtoken) - require.NoError(t, s.ACLTokenDeleteByAccessor(3, "f1093997-b6c7-496d-bfb8-6b1b1895641b")) + require.NoError(t, s.ACLTokenDeleteByAccessor(3, "f1093997-b6c7-496d-bfb8-6b1b1895641b", nil)) - _, rtoken, err = s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b") + _, rtoken, err = s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b", nil) require.NoError(t, err) require.Nil(t, rtoken) }) @@ -1526,13 +1526,13 @@ func TestStateStore_ACLToken_Delete(t *testing.T) { require.NoError(t, s.ACLTokenSet(2, token.Clone(), false)) - _, rtoken, err := s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b") + _, rtoken, err := s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b", nil) require.NoError(t, err) require.NotNil(t, rtoken) - require.NoError(t, s.ACLTokenDeleteBySecret(3, "34ec8eb3-095d-417a-a937-b439af7a8e8b")) + require.NoError(t, s.ACLTokenDeleteBySecret(3, "34ec8eb3-095d-417a-a937-b439af7a8e8b", nil)) - _, rtoken, err = s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b") + _, rtoken, err = s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b", nil) require.NoError(t, err) require.Nil(t, rtoken) }) @@ -1566,10 +1566,10 @@ func TestStateStore_ACLToken_Delete(t *testing.T) { require.NoError(t, s.ACLTokenBatchSet(2, tokens, false, false, false)) - _, rtoken, err := s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b") + _, rtoken, err := s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b", nil) require.NoError(t, err) require.NotNil(t, rtoken) - _, rtoken, err = s.ACLTokenGetByAccessor(nil, "a0bfe8d4-b2f3-4b48-b387-f28afb820eab") + _, rtoken, err = s.ACLTokenGetByAccessor(nil, "a0bfe8d4-b2f3-4b48-b387-f28afb820eab", nil) require.NoError(t, err) require.NotNil(t, rtoken) @@ -1577,10 +1577,10 @@ func TestStateStore_ACLToken_Delete(t *testing.T) { "f1093997-b6c7-496d-bfb8-6b1b1895641b", "a0bfe8d4-b2f3-4b48-b387-f28afb820eab"})) - _, rtoken, err = s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b") + _, rtoken, err = s.ACLTokenGetByAccessor(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b", nil) require.NoError(t, err) require.Nil(t, rtoken) - _, rtoken, err = s.ACLTokenGetByAccessor(nil, "a0bfe8d4-b2f3-4b48-b387-f28afb820eab") + _, rtoken, err = s.ACLTokenGetByAccessor(nil, "a0bfe8d4-b2f3-4b48-b387-f28afb820eab", nil) require.NoError(t, err) require.Nil(t, rtoken) }) @@ -1589,8 +1589,8 @@ func TestStateStore_ACLToken_Delete(t *testing.T) { t.Parallel() s := testACLTokensStateStore(t) - require.Error(t, s.ACLTokenDeleteByAccessor(3, structs.ACLTokenAnonymousID)) - require.Error(t, s.ACLTokenDeleteBySecret(3, "anonymous")) + require.Error(t, s.ACLTokenDeleteByAccessor(3, structs.ACLTokenAnonymousID, nil)) + require.Error(t, s.ACLTokenDeleteBySecret(3, "anonymous", nil)) }) t.Run("Not Found", func(t *testing.T) { @@ -1598,8 +1598,8 @@ func TestStateStore_ACLToken_Delete(t *testing.T) { s := testACLStateStore(t) // deletion of non-existent policies is not an error - require.NoError(t, s.ACLTokenDeleteByAccessor(3, "ea58a09c-2100-4aef-816b-8ee0ade77dcd")) - require.NoError(t, s.ACLTokenDeleteBySecret(3, "376d0cae-dd50-4213-9668-2c7797a7fb2d")) + require.NoError(t, s.ACLTokenDeleteByAccessor(3, "ea58a09c-2100-4aef-816b-8ee0ade77dcd", nil)) + require.NoError(t, s.ACLTokenDeleteBySecret(3, "376d0cae-dd50-4213-9668-2c7797a7fb2d", nil)) }) } @@ -1675,7 +1675,7 @@ func TestStateStore_ACLPolicy_SetGet(t *testing.T) { require.NoError(t, s.ACLPolicySet(3, &policy)) - _, rpolicy, err := s.ACLPolicyGetByName(nil, "management") + _, rpolicy, err := s.ACLPolicyGetByName(nil, "management", nil) require.NoError(t, err) require.NotNil(t, rpolicy) require.Equal(t, structs.ACLPolicyGlobalManagementID, rpolicy.ID) @@ -1702,7 +1702,7 @@ func TestStateStore_ACLPolicy_SetGet(t *testing.T) { require.NoError(t, s.ACLPolicySet(3, &policy)) - idx, rpolicy, err := s.ACLPolicyGetByID(nil, testPolicyID_A) + idx, rpolicy, err := s.ACLPolicyGetByID(nil, testPolicyID_A, nil) require.Equal(t, uint64(3), idx) require.NoError(t, err) require.NotNil(t, rpolicy) @@ -1716,7 +1716,7 @@ func TestStateStore_ACLPolicy_SetGet(t *testing.T) { require.Equal(t, uint64(3), rpolicy.ModifyIndex) // also verify the global management policy that testACLStateStore Set while we are at it. - idx, rpolicy, err = s.ACLPolicyGetByID(nil, structs.ACLPolicyGlobalManagementID) + idx, rpolicy, err = s.ACLPolicyGetByID(nil, structs.ACLPolicyGlobalManagementID, nil) require.Equal(t, uint64(3), idx) require.NoError(t, err) require.NotNil(t, rpolicy) @@ -1750,19 +1750,19 @@ func TestStateStore_ACLPolicy_SetGet(t *testing.T) { expect.ModifyIndex = 3 // policy found via id - idx, rpolicy, err := s.ACLPolicyGetByID(nil, testPolicyID_A) + idx, rpolicy, err := s.ACLPolicyGetByID(nil, testPolicyID_A, nil) require.NoError(t, err) require.Equal(t, uint64(3), idx) require.Equal(t, expect, rpolicy) // policy no longer found via old name - idx, rpolicy, err = s.ACLPolicyGetByName(nil, "node-read") + idx, rpolicy, err = s.ACLPolicyGetByName(nil, "node-read", nil) require.Equal(t, uint64(3), idx) require.NoError(t, err) require.Nil(t, rpolicy) // policy is found via new name - idx, rpolicy, err = s.ACLPolicyGetByName(nil, "node-read-modified") + idx, rpolicy, err = s.ACLPolicyGetByName(nil, "node-read-modified", nil) require.NoError(t, err) require.Equal(t, uint64(3), idx) require.Equal(t, expect, rpolicy) @@ -1892,7 +1892,7 @@ func TestStateStore_ACLPolicy_List(t *testing.T) { require.NoError(t, s.ACLPolicyBatchSet(2, policies)) - _, policies, err := s.ACLPolicyList(nil) + _, policies, err := s.ACLPolicyList(nil, nil) require.NoError(t, err) require.Len(t, policies, 3) policies.Sort() @@ -1936,14 +1936,14 @@ func TestStateStore_ACLPolicy_Delete(t *testing.T) { require.NoError(t, s.ACLPolicySet(2, policy)) - _, rpolicy, err := s.ACLPolicyGetByID(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b") + _, rpolicy, err := s.ACLPolicyGetByID(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b", nil) require.NoError(t, err) require.NotNil(t, rpolicy) - require.NoError(t, s.ACLPolicyDeleteByID(3, "f1093997-b6c7-496d-bfb8-6b1b1895641b")) + require.NoError(t, s.ACLPolicyDeleteByID(3, "f1093997-b6c7-496d-bfb8-6b1b1895641b", nil)) require.NoError(t, err) - _, rpolicy, err = s.ACLPolicyGetByID(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b") + _, rpolicy, err = s.ACLPolicyGetByID(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b", nil) require.NoError(t, err) require.Nil(t, rpolicy) }) @@ -1960,14 +1960,14 @@ func TestStateStore_ACLPolicy_Delete(t *testing.T) { require.NoError(t, s.ACLPolicySet(2, policy)) - _, rpolicy, err := s.ACLPolicyGetByName(nil, "test-policy") + _, rpolicy, err := s.ACLPolicyGetByName(nil, "test-policy", nil) require.NoError(t, err) require.NotNil(t, rpolicy) - require.NoError(t, s.ACLPolicyDeleteByName(3, "test-policy")) + require.NoError(t, s.ACLPolicyDeleteByName(3, "test-policy", nil)) require.NoError(t, err) - _, rpolicy, err = s.ACLPolicyGetByName(nil, "test-policy") + _, rpolicy, err = s.ACLPolicyGetByName(nil, "test-policy", nil) require.NoError(t, err) require.Nil(t, rpolicy) }) @@ -1991,10 +1991,10 @@ func TestStateStore_ACLPolicy_Delete(t *testing.T) { require.NoError(t, s.ACLPolicyBatchSet(2, policies)) - _, rpolicy, err := s.ACLPolicyGetByID(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b") + _, rpolicy, err := s.ACLPolicyGetByID(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b", nil) require.NoError(t, err) require.NotNil(t, rpolicy) - _, rpolicy, err = s.ACLPolicyGetByID(nil, "a0bfe8d4-b2f3-4b48-b387-f28afb820eab") + _, rpolicy, err = s.ACLPolicyGetByID(nil, "a0bfe8d4-b2f3-4b48-b387-f28afb820eab", nil) require.NoError(t, err) require.NotNil(t, rpolicy) @@ -2002,10 +2002,10 @@ func TestStateStore_ACLPolicy_Delete(t *testing.T) { "f1093997-b6c7-496d-bfb8-6b1b1895641b", "a0bfe8d4-b2f3-4b48-b387-f28afb820eab"})) - _, rpolicy, err = s.ACLPolicyGetByID(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b") + _, rpolicy, err = s.ACLPolicyGetByID(nil, "f1093997-b6c7-496d-bfb8-6b1b1895641b", nil) require.NoError(t, err) require.Nil(t, rpolicy) - _, rpolicy, err = s.ACLPolicyGetByID(nil, "a0bfe8d4-b2f3-4b48-b387-f28afb820eab") + _, rpolicy, err = s.ACLPolicyGetByID(nil, "a0bfe8d4-b2f3-4b48-b387-f28afb820eab", nil) require.NoError(t, err) require.Nil(t, rpolicy) }) @@ -2014,8 +2014,8 @@ func TestStateStore_ACLPolicy_Delete(t *testing.T) { t.Parallel() s := testACLStateStore(t) - require.Error(t, s.ACLPolicyDeleteByID(5, structs.ACLPolicyGlobalManagementID)) - require.Error(t, s.ACLPolicyDeleteByName(5, "global-management")) + require.Error(t, s.ACLPolicyDeleteByID(5, structs.ACLPolicyGlobalManagementID, nil)) + require.Error(t, s.ACLPolicyDeleteByName(5, "global-management", nil)) }) t.Run("Not Found", func(t *testing.T) { @@ -2023,8 +2023,8 @@ func TestStateStore_ACLPolicy_Delete(t *testing.T) { s := testACLStateStore(t) // deletion of non-existent policies is not an error - require.NoError(t, s.ACLPolicyDeleteByName(3, "not-found")) - require.NoError(t, s.ACLPolicyDeleteByID(3, "376d0cae-dd50-4213-9668-2c7797a7fb2d")) + require.NoError(t, s.ACLPolicyDeleteByName(3, "not-found", nil)) + require.NoError(t, s.ACLPolicyDeleteByID(3, "376d0cae-dd50-4213-9668-2c7797a7fb2d", nil)) }) } @@ -2162,10 +2162,10 @@ func TestStateStore_ACLRole_SetGet(t *testing.T) { require.Equal(t, "node-read", rrole.Policies[0].Name) } - idx, rpolicy, err := s.ACLRoleGetByID(nil, testRoleID_A) + idx, rpolicy, err := s.ACLRoleGetByID(nil, testRoleID_A, nil) verify(idx, rpolicy, err) - idx, rpolicy, err = s.ACLRoleGetByName(nil, "my-new-role") + idx, rpolicy, err = s.ACLRoleGetByName(nil, "my-new-role", nil) verify(idx, rpolicy, err) }) @@ -2218,17 +2218,17 @@ func TestStateStore_ACLRole_SetGet(t *testing.T) { } // role found via id - idx, rrole, err := s.ACLRoleGetByID(nil, testRoleID_A) + idx, rrole, err := s.ACLRoleGetByID(nil, testRoleID_A, nil) verify(idx, rrole, err) // role no longer found via old name - idx, rrole, err = s.ACLRoleGetByName(nil, "node-read-role") + idx, rrole, err = s.ACLRoleGetByName(nil, "node-read-role", nil) require.Equal(t, uint64(3), idx) require.NoError(t, err) require.Nil(t, rrole) // role is found via new name - idx, rrole, err = s.ACLRoleGetByName(nil, "node-read-role-modified") + idx, rrole, err = s.ACLRoleGetByName(nil, "node-read-role-modified", nil) verify(idx, rrole, err) }) } @@ -2461,7 +2461,7 @@ func TestStateStore_ACLRole_List(t *testing.T) { tc := tc // capture range variable t.Run(tc.name, func(t *testing.T) { // t.Parallel() - _, rroles, err := s.ACLRoleList(nil, tc.policy) + _, rroles, err := s.ACLRoleList(nil, tc.policy, nil) require.NoError(t, err) require.Len(t, rroles, len(tc.ids)) @@ -2515,7 +2515,7 @@ func TestStateStore_ACLRole_FixupPolicyLinks(t *testing.T) { require.NoError(t, s.ACLRoleSet(2, role)) - _, retrieved, err := s.ACLRoleGetByID(nil, role.ID) + _, retrieved, err := s.ACLRoleGetByID(nil, role.ID, nil) require.NoError(t, err) // pointer equality check these should be identical require.True(t, role == retrieved) @@ -2534,7 +2534,7 @@ func TestStateStore_ACLRole_FixupPolicyLinks(t *testing.T) { require.NoError(t, s.ACLPolicySet(3, renamed)) // retrieve the role again - _, retrieved, err = s.ACLRoleGetByID(nil, role.ID) + _, retrieved, err = s.ACLRoleGetByID(nil, role.ID, nil) require.NoError(t, err) // pointer equality check these should be different if we cloned things appropriately require.True(t, role != retrieved) @@ -2542,7 +2542,7 @@ func TestStateStore_ACLRole_FixupPolicyLinks(t *testing.T) { require.Equal(t, "node-read-renamed", retrieved.Policies[0].Name) // list roles without stale links - _, roles, err := s.ACLRoleList(nil, "") + _, roles, err := s.ACLRoleList(nil, "", nil) require.NoError(t, err) found := false @@ -2576,17 +2576,17 @@ func TestStateStore_ACLRole_FixupPolicyLinks(t *testing.T) { require.True(t, found) // delete the policy - require.NoError(t, s.ACLPolicyDeleteByID(4, testPolicyID_A)) + require.NoError(t, s.ACLPolicyDeleteByID(4, testPolicyID_A, nil)) // retrieve the role again - _, retrieved, err = s.ACLRoleGetByID(nil, role.ID) + _, retrieved, err = s.ACLRoleGetByID(nil, role.ID, nil) require.NoError(t, err) // pointer equality check these should be different if we cloned things appropriately require.True(t, role != retrieved) require.Len(t, retrieved.Policies, 0) // list roles without stale links - _, roles, err = s.ACLRoleList(nil, "") + _, roles, err = s.ACLRoleList(nil, "", nil) require.NoError(t, err) found = false @@ -2638,14 +2638,14 @@ func TestStateStore_ACLRole_Delete(t *testing.T) { require.NoError(t, s.ACLRoleSet(2, role)) - _, rrole, err := s.ACLRoleGetByID(nil, testRoleID_A) + _, rrole, err := s.ACLRoleGetByID(nil, testRoleID_A, nil) require.NoError(t, err) require.NotNil(t, rrole) - require.NoError(t, s.ACLRoleDeleteByID(3, testRoleID_A)) + require.NoError(t, s.ACLRoleDeleteByID(3, testRoleID_A, nil)) require.NoError(t, err) - _, rrole, err = s.ACLRoleGetByID(nil, testRoleID_A) + _, rrole, err = s.ACLRoleGetByID(nil, testRoleID_A, nil) require.NoError(t, err) require.Nil(t, rrole) }) @@ -2667,14 +2667,14 @@ func TestStateStore_ACLRole_Delete(t *testing.T) { require.NoError(t, s.ACLRoleSet(2, role)) - _, rrole, err := s.ACLRoleGetByName(nil, "role1") + _, rrole, err := s.ACLRoleGetByName(nil, "role1", nil) require.NoError(t, err) require.NotNil(t, rrole) - require.NoError(t, s.ACLRoleDeleteByName(3, "role1")) + require.NoError(t, s.ACLRoleDeleteByName(3, "role1", nil)) require.NoError(t, err) - _, rrole, err = s.ACLRoleGetByName(nil, "role1") + _, rrole, err = s.ACLRoleGetByName(nil, "role1", nil) require.NoError(t, err) require.Nil(t, rrole) }) @@ -2708,19 +2708,19 @@ func TestStateStore_ACLRole_Delete(t *testing.T) { require.NoError(t, s.ACLRoleBatchSet(2, roles, false)) - _, rrole, err := s.ACLRoleGetByID(nil, testRoleID_A) + _, rrole, err := s.ACLRoleGetByID(nil, testRoleID_A, nil) require.NoError(t, err) require.NotNil(t, rrole) - _, rrole, err = s.ACLRoleGetByID(nil, testRoleID_B) + _, rrole, err = s.ACLRoleGetByID(nil, testRoleID_B, nil) require.NoError(t, err) require.NotNil(t, rrole) require.NoError(t, s.ACLRoleBatchDelete(3, []string{testRoleID_A, testRoleID_B})) - _, rrole, err = s.ACLRoleGetByID(nil, testRoleID_A) + _, rrole, err = s.ACLRoleGetByID(nil, testRoleID_A, nil) require.NoError(t, err) require.Nil(t, rrole) - _, rrole, err = s.ACLRoleGetByID(nil, testRoleID_B) + _, rrole, err = s.ACLRoleGetByID(nil, testRoleID_B, nil) require.NoError(t, err) require.Nil(t, rrole) }) @@ -2730,8 +2730,8 @@ func TestStateStore_ACLRole_Delete(t *testing.T) { s := testACLStateStore(t) // deletion of non-existent roles is not an error - require.NoError(t, s.ACLRoleDeleteByName(3, "not-found")) - require.NoError(t, s.ACLRoleDeleteByID(3, testRoleID_A)) + require.NoError(t, s.ACLRoleDeleteByName(3, "not-found", nil)) + require.NoError(t, s.ACLRoleDeleteByID(3, testRoleID_A, nil)) }) } @@ -2779,7 +2779,7 @@ func TestStateStore_ACLAuthMethod_SetGet(t *testing.T) { require.NoError(t, s.ACLAuthMethodSet(3, &method)) - idx, rmethod, err := s.ACLAuthMethodGetByName(nil, "test") + idx, rmethod, err := s.ACLAuthMethodGetByName(nil, "test", nil) require.NoError(t, err) require.Equal(t, uint64(3), idx) require.NotNil(t, rmethod) @@ -2815,7 +2815,7 @@ func TestStateStore_ACLAuthMethod_SetGet(t *testing.T) { require.NoError(t, s.ACLAuthMethodSet(3, &update)) - idx, rmethod, err := s.ACLAuthMethodGetByName(nil, "test") + idx, rmethod, err := s.ACLAuthMethodGetByName(nil, "test", nil) require.NoError(t, err) require.Equal(t, uint64(3), idx) require.NotNil(t, rmethod) @@ -2850,7 +2850,7 @@ func TestStateStore_ACLAuthMethods_UpsertBatchRead(t *testing.T) { require.NoError(t, s.ACLAuthMethodBatchSet(2, methods)) - idx, rmethods, err := s.ACLAuthMethodList(nil) + idx, rmethods, err := s.ACLAuthMethodList(nil, nil) require.NoError(t, err) require.Equal(t, uint64(2), idx) require.Len(t, rmethods, 2) @@ -2904,7 +2904,7 @@ func TestStateStore_ACLAuthMethods_UpsertBatchRead(t *testing.T) { require.NoError(t, s.ACLAuthMethodBatchSet(3, updates)) - idx, rmethods, err := s.ACLAuthMethodList(nil) + idx, rmethods, err := s.ACLAuthMethodList(nil, nil) require.NoError(t, err) require.Equal(t, uint64(3), idx) require.Len(t, rmethods, 2) @@ -2936,7 +2936,7 @@ func TestStateStore_ACLAuthMethod_List(t *testing.T) { require.NoError(t, s.ACLAuthMethodBatchSet(2, methods)) - _, rmethods, err := s.ACLAuthMethodList(nil) + _, rmethods, err := s.ACLAuthMethodList(nil, nil) require.NoError(t, err) require.Len(t, rmethods, 2) @@ -2970,14 +2970,14 @@ func TestStateStore_ACLAuthMethod_Delete(t *testing.T) { require.NoError(t, s.ACLAuthMethodSet(2, &method)) - _, rmethod, err := s.ACLAuthMethodGetByName(nil, "test") + _, rmethod, err := s.ACLAuthMethodGetByName(nil, "test", nil) require.NoError(t, err) require.NotNil(t, rmethod) - require.NoError(t, s.ACLAuthMethodDeleteByName(3, "test")) + require.NoError(t, s.ACLAuthMethodDeleteByName(3, "test", nil)) require.NoError(t, err) - _, rmethod, err = s.ACLAuthMethodGetByName(nil, "test") + _, rmethod, err = s.ACLAuthMethodGetByName(nil, "test", nil) require.NoError(t, err) require.Nil(t, rmethod) }) @@ -3001,19 +3001,19 @@ func TestStateStore_ACLAuthMethod_Delete(t *testing.T) { require.NoError(t, s.ACLAuthMethodBatchSet(2, methods)) - _, rmethod, err := s.ACLAuthMethodGetByName(nil, "test-1") + _, rmethod, err := s.ACLAuthMethodGetByName(nil, "test-1", nil) require.NoError(t, err) require.NotNil(t, rmethod) - _, rmethod, err = s.ACLAuthMethodGetByName(nil, "test-2") + _, rmethod, err = s.ACLAuthMethodGetByName(nil, "test-2", nil) require.NoError(t, err) require.NotNil(t, rmethod) - require.NoError(t, s.ACLAuthMethodBatchDelete(3, []string{"test-1", "test-2"})) + require.NoError(t, s.ACLAuthMethodBatchDelete(3, []string{"test-1", "test-2"}, nil)) - _, rmethod, err = s.ACLAuthMethodGetByName(nil, "test-1") + _, rmethod, err = s.ACLAuthMethodGetByName(nil, "test-1", nil) require.NoError(t, err) require.Nil(t, rmethod) - _, rmethod, err = s.ACLAuthMethodGetByName(nil, "test-2") + _, rmethod, err = s.ACLAuthMethodGetByName(nil, "test-2", nil) require.NoError(t, err) require.Nil(t, rmethod) }) @@ -3023,7 +3023,7 @@ func TestStateStore_ACLAuthMethod_Delete(t *testing.T) { s := testACLStateStore(t) // deletion of non-existent methods is not an error - require.NoError(t, s.ACLAuthMethodDeleteByName(3, "not-found")) + require.NoError(t, s.ACLAuthMethodDeleteByName(3, "not-found", nil)) }) } @@ -3114,33 +3114,33 @@ func TestStateStore_ACLAuthMethod_Delete_RuleAndTokenCascade(t *testing.T) { require.NoError(t, s.ACLTokenBatchSet(4, tokens, false, false, false)) // Delete one method. - require.NoError(t, s.ACLAuthMethodDeleteByName(4, "test-1")) + require.NoError(t, s.ACLAuthMethodDeleteByName(4, "test-1", nil)) // Make sure the method is gone. - _, rmethod, err := s.ACLAuthMethodGetByName(nil, "test-1") + _, rmethod, err := s.ACLAuthMethodGetByName(nil, "test-1", nil) require.NoError(t, err) require.Nil(t, rmethod) // Make sure the rules and tokens are gone. for _, ruleID := range []string{method1_rule1, method1_rule2} { - _, rrule, err := s.ACLBindingRuleGetByID(nil, ruleID) + _, rrule, err := s.ACLBindingRuleGetByID(nil, ruleID, nil) require.NoError(t, err) require.Nil(t, rrule) } for _, tokID := range []string{method1_tok1, method1_tok2} { - _, tok, err := s.ACLTokenGetByAccessor(nil, tokID) + _, tok, err := s.ACLTokenGetByAccessor(nil, tokID, nil) require.NoError(t, err) require.Nil(t, tok) } // Make sure the rules and tokens for the untouched method are still there. for _, ruleID := range []string{method2_rule1, method2_rule2} { - _, rrule, err := s.ACLBindingRuleGetByID(nil, ruleID) + _, rrule, err := s.ACLBindingRuleGetByID(nil, ruleID, nil) require.NoError(t, err) require.NotNil(t, rrule) } for _, tokID := range []string{method2_tok1, method2_tok2} { - _, tok, err := s.ACLTokenGetByAccessor(nil, tokID) + _, tok, err := s.ACLTokenGetByAccessor(nil, tokID, nil) require.NoError(t, err) require.NotNil(t, tok) } @@ -3207,7 +3207,7 @@ func TestStateStore_ACLBindingRule_SetGet(t *testing.T) { require.NoError(t, s.ACLBindingRuleSet(3, &rule)) - idx, rrule, err := s.ACLBindingRuleGetByID(nil, rule.ID) + idx, rrule, err := s.ACLBindingRuleGetByID(nil, rule.ID, nil) require.NoError(t, err) require.Equal(t, uint64(3), idx) require.NotNil(t, rrule) @@ -3243,7 +3243,7 @@ func TestStateStore_ACLBindingRule_SetGet(t *testing.T) { require.NoError(t, s.ACLBindingRuleSet(3, &update)) - idx, rrule, err := s.ACLBindingRuleGetByID(nil, rule.ID) + idx, rrule, err := s.ACLBindingRuleGetByID(nil, rule.ID, nil) require.NoError(t, err) require.Equal(t, uint64(3), idx) require.NotNil(t, rrule) @@ -3280,7 +3280,7 @@ func TestStateStore_ACLBindingRules_UpsertBatchRead(t *testing.T) { require.NoError(t, s.ACLBindingRuleBatchSet(2, rules)) - idx, rrules, err := s.ACLBindingRuleList(nil, "test") + idx, rrules, err := s.ACLBindingRuleList(nil, "test", nil) require.NoError(t, err) require.Equal(t, uint64(2), idx) require.Len(t, rrules, 2) @@ -3333,7 +3333,7 @@ func TestStateStore_ACLBindingRules_UpsertBatchRead(t *testing.T) { require.NoError(t, s.ACLBindingRuleBatchSet(3, updates)) - idx, rrules, err := s.ACLBindingRuleList(nil, "test") + idx, rrules, err := s.ACLBindingRuleList(nil, "test", nil) require.NoError(t, err) require.Equal(t, uint64(3), idx) require.Len(t, rrules, 2) @@ -3366,7 +3366,7 @@ func TestStateStore_ACLBindingRule_List(t *testing.T) { require.NoError(t, s.ACLBindingRuleBatchSet(2, rules)) - _, rrules, err := s.ACLBindingRuleList(nil, "") + _, rrules, err := s.ACLBindingRuleList(nil, "", nil) require.NoError(t, err) require.Len(t, rrules, 2) @@ -3401,14 +3401,14 @@ func TestStateStore_ACLBindingRule_Delete(t *testing.T) { require.NoError(t, s.ACLBindingRuleSet(2, &rule)) - _, rrule, err := s.ACLBindingRuleGetByID(nil, rule.ID) + _, rrule, err := s.ACLBindingRuleGetByID(nil, rule.ID, nil) require.NoError(t, err) require.NotNil(t, rrule) - require.NoError(t, s.ACLBindingRuleDeleteByID(3, rule.ID)) + require.NoError(t, s.ACLBindingRuleDeleteByID(3, rule.ID, nil)) require.NoError(t, err) - _, rrule, err = s.ACLBindingRuleGetByID(nil, rule.ID) + _, rrule, err = s.ACLBindingRuleGetByID(nil, rule.ID, nil) require.NoError(t, err) require.Nil(t, rrule) }) @@ -3433,19 +3433,19 @@ func TestStateStore_ACLBindingRule_Delete(t *testing.T) { require.NoError(t, s.ACLBindingRuleBatchSet(2, rules)) - _, rrule, err := s.ACLBindingRuleGetByID(nil, rules[0].ID) + _, rrule, err := s.ACLBindingRuleGetByID(nil, rules[0].ID, nil) require.NoError(t, err) require.NotNil(t, rrule) - _, rrule, err = s.ACLBindingRuleGetByID(nil, rules[1].ID) + _, rrule, err = s.ACLBindingRuleGetByID(nil, rules[1].ID, nil) require.NoError(t, err) require.NotNil(t, rrule) require.NoError(t, s.ACLBindingRuleBatchDelete(3, []string{rules[0].ID, rules[1].ID})) - _, rrule, err = s.ACLBindingRuleGetByID(nil, rules[0].ID) + _, rrule, err = s.ACLBindingRuleGetByID(nil, rules[0].ID, nil) require.NoError(t, err) require.Nil(t, rrule) - _, rrule, err = s.ACLBindingRuleGetByID(nil, rules[1].ID) + _, rrule, err = s.ACLBindingRuleGetByID(nil, rules[1].ID, nil) require.NoError(t, err) require.Nil(t, rrule) }) @@ -3455,7 +3455,7 @@ func TestStateStore_ACLBindingRule_Delete(t *testing.T) { s := testACLStateStore(t) // deletion of non-existent rules is not an error - require.NoError(t, s.ACLBindingRuleDeleteByID(3, "ed3ce1b8-3a16-4e2f-b82e-f92e3b92410d")) + require.NoError(t, s.ACLBindingRuleDeleteByID(3, "ed3ce1b8-3a16-4e2f-b82e-f92e3b92410d", nil)) }) } @@ -3576,7 +3576,7 @@ func TestStateStore_ACLTokens_Snapshot_Restore(t *testing.T) { defer snap.Close() // Alter the real state store. - require.NoError(t, s.ACLTokenDeleteByAccessor(3, tokens[0].AccessorID)) + require.NoError(t, s.ACLTokenDeleteByAccessor(3, tokens[0].AccessorID, nil)) // Verify the snapshot. require.Equal(t, uint64(4), snap.LastIndex()) @@ -3590,6 +3590,9 @@ func TestStateStore_ACLTokens_Snapshot_Restore(t *testing.T) { } require.ElementsMatch(t, dump, tokens) + indexes, err := snapshotIndexes(snap) + require.NoError(t, err) + // Restore the values into a new state store. func() { s := testStateStore(t) @@ -3597,6 +3600,7 @@ func TestStateStore_ACLTokens_Snapshot_Restore(t *testing.T) { for _, token := range dump { require.NoError(t, restore.ACLToken(token)) } + require.NoError(t, restoreIndexes(indexes, restore)) restore.Commit() // need to ensure we have the policies or else the links will be removed @@ -3606,7 +3610,7 @@ func TestStateStore_ACLTokens_Snapshot_Restore(t *testing.T) { require.NoError(t, s.ACLRoleBatchSet(2, roles, false)) // Read the restored ACLs back out and verify that they match. - idx, res, err := s.ACLTokenList(nil, true, true, "", "", "") + idx, res, err := s.ACLTokenList(nil, true, true, "", "", "", nil) require.NoError(t, err) require.Equal(t, uint64(4), idx) require.ElementsMatch(t, tokens, res) @@ -3643,7 +3647,7 @@ func TestStateStore_ACLPolicies_Snapshot_Restore(t *testing.T) { defer snap.Close() // Alter the real state store. - require.NoError(t, s.ACLPolicyDeleteByID(3, policies[0].ID)) + require.NoError(t, s.ACLPolicyDeleteByID(3, policies[0].ID, nil)) // Verify the snapshot. require.Equal(t, uint64(2), snap.LastIndex()) @@ -3657,6 +3661,9 @@ func TestStateStore_ACLPolicies_Snapshot_Restore(t *testing.T) { } require.ElementsMatch(t, dump, policies) + indexes, err := snapshotIndexes(snap) + require.NoError(t, err) + // Restore the values into a new state store. func() { s := testStateStore(t) @@ -3664,10 +3671,11 @@ func TestStateStore_ACLPolicies_Snapshot_Restore(t *testing.T) { for _, policy := range dump { require.NoError(t, restore.ACLPolicy(policy)) } + require.NoError(t, restoreIndexes(indexes, restore)) restore.Commit() // Read the restored ACLs back out and verify that they match. - idx, res, err := s.ACLPolicyList(nil) + idx, res, err := s.ACLPolicyList(nil, nil) require.NoError(t, err) require.Equal(t, uint64(2), idx) require.ElementsMatch(t, policies, res) @@ -3912,7 +3920,7 @@ func TestStateStore_ACLRoles_Snapshot_Restore(t *testing.T) { defer snap.Close() // Alter the real state store. - require.NoError(t, s.ACLRoleDeleteByID(3, roles[0].ID)) + require.NoError(t, s.ACLRoleDeleteByID(3, roles[0].ID, nil)) // Verify the snapshot. require.Equal(t, uint64(2), snap.LastIndex()) @@ -3926,6 +3934,9 @@ func TestStateStore_ACLRoles_Snapshot_Restore(t *testing.T) { } require.ElementsMatch(t, dump, roles) + indexes, err := snapshotIndexes(snap) + require.NoError(t, err) + // Restore the values into a new state store. func() { s := testStateStore(t) @@ -3933,13 +3944,14 @@ func TestStateStore_ACLRoles_Snapshot_Restore(t *testing.T) { for _, role := range dump { require.NoError(t, restore.ACLRole(role)) } + require.NoError(t, restoreIndexes(indexes, restore)) restore.Commit() // need to ensure we have the policies or else the links will be removed require.NoError(t, s.ACLPolicyBatchSet(2, policies)) // Read the restored ACLs back out and verify that they match. - idx, res, err := s.ACLRoleList(nil, "") + idx, res, err := s.ACLRoleList(nil, "", nil) require.NoError(t, err) require.Equal(t, uint64(2), idx) require.ElementsMatch(t, roles, res) @@ -3972,7 +3984,7 @@ func TestStateStore_ACLAuthMethods_Snapshot_Restore(t *testing.T) { defer snap.Close() // Alter the real state store. - require.NoError(t, s.ACLAuthMethodDeleteByName(3, "test-1")) + require.NoError(t, s.ACLAuthMethodDeleteByName(3, "test-1", nil)) // Verify the snapshot. require.Equal(t, uint64(2), snap.LastIndex()) @@ -3986,6 +3998,9 @@ func TestStateStore_ACLAuthMethods_Snapshot_Restore(t *testing.T) { } require.ElementsMatch(t, dump, methods) + indexes, err := snapshotIndexes(snap) + require.NoError(t, err) + // Restore the values into a new state store. func() { s := testStateStore(t) @@ -3993,10 +4008,11 @@ func TestStateStore_ACLAuthMethods_Snapshot_Restore(t *testing.T) { for _, method := range dump { require.NoError(t, restore.ACLAuthMethod(method)) } + require.NoError(t, restoreIndexes(indexes, restore)) restore.Commit() // Read the restored methods back out and verify that they match. - idx, res, err := s.ACLAuthMethodList(nil) + idx, res, err := s.ACLAuthMethodList(nil, nil) require.NoError(t, err) require.Equal(t, uint64(2), idx) require.ElementsMatch(t, methods, res) @@ -4030,7 +4046,7 @@ func TestStateStore_ACLBindingRules_Snapshot_Restore(t *testing.T) { defer snap.Close() // Alter the real state store. - require.NoError(t, s.ACLBindingRuleDeleteByID(3, rules[0].ID)) + require.NoError(t, s.ACLBindingRuleDeleteByID(3, rules[0].ID, nil)) // Verify the snapshot. require.Equal(t, uint64(2), snap.LastIndex()) @@ -4044,6 +4060,9 @@ func TestStateStore_ACLBindingRules_Snapshot_Restore(t *testing.T) { } require.ElementsMatch(t, dump, rules) + indexes, err := snapshotIndexes(snap) + require.NoError(t, err) + // Restore the values into a new state store. func() { s := testStateStore(t) @@ -4053,10 +4072,11 @@ func TestStateStore_ACLBindingRules_Snapshot_Restore(t *testing.T) { for _, rule := range dump { require.NoError(t, restore.ACLBindingRule(rule)) } + require.NoError(t, restoreIndexes(indexes, restore)) restore.Commit() // Read the restored rules back out and verify that they match. - idx, res, err := s.ACLBindingRuleList(nil, "") + idx, res, err := s.ACLBindingRuleList(nil, "", nil) require.NoError(t, err) require.Equal(t, uint64(2), idx) require.ElementsMatch(t, rules, res) diff --git a/agent/consul/state/state_store_test.go b/agent/consul/state/state_store_test.go index 6145249503..b35baee38e 100644 --- a/agent/consul/state/state_store_test.go +++ b/agent/consul/state/state_store_test.go @@ -26,6 +26,27 @@ func testUUID() string { buf[10:16]) } +func snapshotIndexes(snap *Snapshot) ([]*IndexEntry, error) { + iter, err := snap.Indexes() + if err != nil { + return nil, err + } + var indexes []*IndexEntry + for index := iter.Next(); index != nil; index = iter.Next() { + indexes = append(indexes, index.(*IndexEntry)) + } + return indexes, nil +} + +func restoreIndexes(indexes []*IndexEntry, r *Restore) error { + for _, index := range indexes { + if err := r.IndexRestore(index); err != nil { + return err + } + } + return nil +} + func testStateStore(t *testing.T) *Store { s, err := NewStateStore(nil) if err != nil { diff --git a/agent/http.go b/agent/http.go index 8a3943c8ef..25bfd626b2 100644 --- a/agent/http.go +++ b/agent/http.go @@ -192,7 +192,7 @@ var endpoints map[string]unboundEndpoint // allowedMethods is a map from endpoint prefix to supported HTTP methods. // An empty slice means an endpoint handles OPTIONS requests and MethodNotFound errors itself. -var allowedMethods map[string][]string +var allowedMethods map[string][]string = make(map[string][]string) // registerEndpoint registers a new endpoint, which should be done at package // init() time. diff --git a/agent/http_oss.go b/agent/http_oss.go index 9bfa6b6e8f..c770ce152a 100644 --- a/agent/http_oss.go +++ b/agent/http_oss.go @@ -1,114 +1,12 @@ +// +build !consulent + package agent -func init() { - allowedMethods = make(map[string][]string) +import ( + "net/http" - registerEndpoint("/v1/acl/bootstrap", []string{"PUT"}, (*HTTPServer).ACLBootstrap) - registerEndpoint("/v1/acl/create", []string{"PUT"}, (*HTTPServer).ACLCreate) - registerEndpoint("/v1/acl/update", []string{"PUT"}, (*HTTPServer).ACLUpdate) - registerEndpoint("/v1/acl/destroy/", []string{"PUT"}, (*HTTPServer).ACLDestroy) - registerEndpoint("/v1/acl/info/", []string{"GET"}, (*HTTPServer).ACLGet) - registerEndpoint("/v1/acl/clone/", []string{"PUT"}, (*HTTPServer).ACLClone) - registerEndpoint("/v1/acl/list", []string{"GET"}, (*HTTPServer).ACLList) - registerEndpoint("/v1/acl/login", []string{"POST"}, (*HTTPServer).ACLLogin) - registerEndpoint("/v1/acl/logout", []string{"POST"}, (*HTTPServer).ACLLogout) - registerEndpoint("/v1/acl/replication", []string{"GET"}, (*HTTPServer).ACLReplicationStatus) - registerEndpoint("/v1/acl/policies", []string{"GET"}, (*HTTPServer).ACLPolicyList) - registerEndpoint("/v1/acl/policy", []string{"PUT"}, (*HTTPServer).ACLPolicyCreate) - registerEndpoint("/v1/acl/policy/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).ACLPolicyCRUD) - registerEndpoint("/v1/acl/roles", []string{"GET"}, (*HTTPServer).ACLRoleList) - registerEndpoint("/v1/acl/role", []string{"PUT"}, (*HTTPServer).ACLRoleCreate) - registerEndpoint("/v1/acl/role/name/", []string{"GET"}, (*HTTPServer).ACLRoleReadByName) - registerEndpoint("/v1/acl/role/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).ACLRoleCRUD) - registerEndpoint("/v1/acl/binding-rules", []string{"GET"}, (*HTTPServer).ACLBindingRuleList) - registerEndpoint("/v1/acl/binding-rule", []string{"PUT"}, (*HTTPServer).ACLBindingRuleCreate) - registerEndpoint("/v1/acl/binding-rule/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).ACLBindingRuleCRUD) - registerEndpoint("/v1/acl/auth-methods", []string{"GET"}, (*HTTPServer).ACLAuthMethodList) - registerEndpoint("/v1/acl/auth-method", []string{"PUT"}, (*HTTPServer).ACLAuthMethodCreate) - registerEndpoint("/v1/acl/auth-method/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).ACLAuthMethodCRUD) - registerEndpoint("/v1/acl/rules/translate", []string{"POST"}, (*HTTPServer).ACLRulesTranslate) - registerEndpoint("/v1/acl/rules/translate/", []string{"GET"}, (*HTTPServer).ACLRulesTranslateLegacyToken) - registerEndpoint("/v1/acl/tokens", []string{"GET"}, (*HTTPServer).ACLTokenList) - registerEndpoint("/v1/acl/token", []string{"PUT"}, (*HTTPServer).ACLTokenCreate) - registerEndpoint("/v1/acl/token/self", []string{"GET"}, (*HTTPServer).ACLTokenSelf) - registerEndpoint("/v1/acl/token/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).ACLTokenCRUD) - registerEndpoint("/v1/agent/token/", []string{"PUT"}, (*HTTPServer).AgentToken) - registerEndpoint("/v1/agent/self", []string{"GET"}, (*HTTPServer).AgentSelf) - registerEndpoint("/v1/agent/host", []string{"GET"}, (*HTTPServer).AgentHost) - registerEndpoint("/v1/agent/maintenance", []string{"PUT"}, (*HTTPServer).AgentNodeMaintenance) - registerEndpoint("/v1/agent/reload", []string{"PUT"}, (*HTTPServer).AgentReload) - registerEndpoint("/v1/agent/monitor", []string{"GET"}, (*HTTPServer).AgentMonitor) - registerEndpoint("/v1/agent/metrics", []string{"GET"}, (*HTTPServer).AgentMetrics) - registerEndpoint("/v1/agent/services", []string{"GET"}, (*HTTPServer).AgentServices) - registerEndpoint("/v1/agent/service/", []string{"GET"}, (*HTTPServer).AgentService) - registerEndpoint("/v1/agent/checks", []string{"GET"}, (*HTTPServer).AgentChecks) - registerEndpoint("/v1/agent/members", []string{"GET"}, (*HTTPServer).AgentMembers) - registerEndpoint("/v1/agent/join/", []string{"PUT"}, (*HTTPServer).AgentJoin) - registerEndpoint("/v1/agent/leave", []string{"PUT"}, (*HTTPServer).AgentLeave) - registerEndpoint("/v1/agent/force-leave/", []string{"PUT"}, (*HTTPServer).AgentForceLeave) - registerEndpoint("/v1/agent/health/service/id/", []string{"GET"}, (*HTTPServer).AgentHealthServiceByID) - registerEndpoint("/v1/agent/health/service/name/", []string{"GET"}, (*HTTPServer).AgentHealthServiceByName) - registerEndpoint("/v1/agent/check/register", []string{"PUT"}, (*HTTPServer).AgentRegisterCheck) - registerEndpoint("/v1/agent/check/deregister/", []string{"PUT"}, (*HTTPServer).AgentDeregisterCheck) - registerEndpoint("/v1/agent/check/pass/", []string{"PUT"}, (*HTTPServer).AgentCheckPass) - registerEndpoint("/v1/agent/check/warn/", []string{"PUT"}, (*HTTPServer).AgentCheckWarn) - registerEndpoint("/v1/agent/check/fail/", []string{"PUT"}, (*HTTPServer).AgentCheckFail) - registerEndpoint("/v1/agent/check/update/", []string{"PUT"}, (*HTTPServer).AgentCheckUpdate) - registerEndpoint("/v1/agent/connect/authorize", []string{"POST"}, (*HTTPServer).AgentConnectAuthorize) - registerEndpoint("/v1/agent/connect/ca/roots", []string{"GET"}, (*HTTPServer).AgentConnectCARoots) - registerEndpoint("/v1/agent/connect/ca/leaf/", []string{"GET"}, (*HTTPServer).AgentConnectCALeafCert) - registerEndpoint("/v1/agent/service/register", []string{"PUT"}, (*HTTPServer).AgentRegisterService) - registerEndpoint("/v1/agent/service/deregister/", []string{"PUT"}, (*HTTPServer).AgentDeregisterService) - registerEndpoint("/v1/agent/service/maintenance/", []string{"PUT"}, (*HTTPServer).AgentServiceMaintenance) - registerEndpoint("/v1/catalog/register", []string{"PUT"}, (*HTTPServer).CatalogRegister) - registerEndpoint("/v1/catalog/connect/", []string{"GET"}, (*HTTPServer).CatalogConnectServiceNodes) - registerEndpoint("/v1/catalog/deregister", []string{"PUT"}, (*HTTPServer).CatalogDeregister) - registerEndpoint("/v1/catalog/datacenters", []string{"GET"}, (*HTTPServer).CatalogDatacenters) - registerEndpoint("/v1/catalog/nodes", []string{"GET"}, (*HTTPServer).CatalogNodes) - registerEndpoint("/v1/catalog/services", []string{"GET"}, (*HTTPServer).CatalogServices) - registerEndpoint("/v1/catalog/service/", []string{"GET"}, (*HTTPServer).CatalogServiceNodes) - registerEndpoint("/v1/catalog/node/", []string{"GET"}, (*HTTPServer).CatalogNodeServices) - registerEndpoint("/v1/config/", []string{"GET", "DELETE"}, (*HTTPServer).Config) - registerEndpoint("/v1/config", []string{"PUT"}, (*HTTPServer).ConfigApply) - registerEndpoint("/v1/connect/ca/configuration", []string{"GET", "PUT"}, (*HTTPServer).ConnectCAConfiguration) - registerEndpoint("/v1/connect/ca/roots", []string{"GET"}, (*HTTPServer).ConnectCARoots) - registerEndpoint("/v1/connect/intentions", []string{"GET", "POST"}, (*HTTPServer).IntentionEndpoint) - registerEndpoint("/v1/connect/intentions/match", []string{"GET"}, (*HTTPServer).IntentionMatch) - registerEndpoint("/v1/connect/intentions/check", []string{"GET"}, (*HTTPServer).IntentionCheck) - registerEndpoint("/v1/connect/intentions/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).IntentionSpecific) - registerEndpoint("/v1/coordinate/datacenters", []string{"GET"}, (*HTTPServer).CoordinateDatacenters) - registerEndpoint("/v1/coordinate/nodes", []string{"GET"}, (*HTTPServer).CoordinateNodes) - registerEndpoint("/v1/coordinate/node/", []string{"GET"}, (*HTTPServer).CoordinateNode) - registerEndpoint("/v1/coordinate/update", []string{"PUT"}, (*HTTPServer).CoordinateUpdate) - registerEndpoint("/v1/discovery-chain/", []string{"GET", "POST"}, (*HTTPServer).DiscoveryChainRead) - registerEndpoint("/v1/event/fire/", []string{"PUT"}, (*HTTPServer).EventFire) - registerEndpoint("/v1/event/list", []string{"GET"}, (*HTTPServer).EventList) - registerEndpoint("/v1/health/node/", []string{"GET"}, (*HTTPServer).HealthNodeChecks) - registerEndpoint("/v1/health/checks/", []string{"GET"}, (*HTTPServer).HealthServiceChecks) - registerEndpoint("/v1/health/state/", []string{"GET"}, (*HTTPServer).HealthChecksInState) - registerEndpoint("/v1/health/service/", []string{"GET"}, (*HTTPServer).HealthServiceNodes) - registerEndpoint("/v1/health/connect/", []string{"GET"}, (*HTTPServer).HealthConnectServiceNodes) - registerEndpoint("/v1/internal/ui/nodes", []string{"GET"}, (*HTTPServer).UINodes) - registerEndpoint("/v1/internal/ui/node/", []string{"GET"}, (*HTTPServer).UINodeInfo) - registerEndpoint("/v1/internal/ui/services", []string{"GET"}, (*HTTPServer).UIServices) - registerEndpoint("/v1/kv/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).KVSEndpoint) - registerEndpoint("/v1/operator/raft/configuration", []string{"GET"}, (*HTTPServer).OperatorRaftConfiguration) - registerEndpoint("/v1/operator/raft/peer", []string{"DELETE"}, (*HTTPServer).OperatorRaftPeer) - registerEndpoint("/v1/operator/keyring", []string{"GET", "POST", "PUT", "DELETE"}, (*HTTPServer).OperatorKeyringEndpoint) - registerEndpoint("/v1/operator/autopilot/configuration", []string{"GET", "PUT"}, (*HTTPServer).OperatorAutopilotConfiguration) - registerEndpoint("/v1/operator/autopilot/health", []string{"GET"}, (*HTTPServer).OperatorServerHealth) - registerEndpoint("/v1/query", []string{"GET", "POST"}, (*HTTPServer).PreparedQueryGeneral) - // specific prepared query endpoints have more complex rules for allowed methods, so - // the prefix is registered with no methods. - registerEndpoint("/v1/query/", []string{}, (*HTTPServer).PreparedQuerySpecific) - registerEndpoint("/v1/session/create", []string{"PUT"}, (*HTTPServer).SessionCreate) - registerEndpoint("/v1/session/destroy/", []string{"PUT"}, (*HTTPServer).SessionDestroy) - registerEndpoint("/v1/session/renew/", []string{"PUT"}, (*HTTPServer).SessionRenew) - registerEndpoint("/v1/session/info/", []string{"GET"}, (*HTTPServer).SessionGet) - registerEndpoint("/v1/session/node/", []string{"GET"}, (*HTTPServer).SessionsForNode) - registerEndpoint("/v1/session/list", []string{"GET"}, (*HTTPServer).SessionList) - registerEndpoint("/v1/status/leader", []string{"GET"}, (*HTTPServer).StatusLeader) - registerEndpoint("/v1/status/peers", []string{"GET"}, (*HTTPServer).StatusPeers) - registerEndpoint("/v1/snapshot", []string{"GET", "PUT"}, (*HTTPServer).Snapshot) - registerEndpoint("/v1/txn", []string{"PUT"}, (*HTTPServer).Txn) + "github.com/hashicorp/consul/agent/structs" +) + +func (s *HTTPServer) parseEntMeta(req *http.Request, entMeta *structs.EnterpriseMeta) { } diff --git a/agent/http_register.go b/agent/http_register.go new file mode 100644 index 0000000000..87c4c716bf --- /dev/null +++ b/agent/http_register.go @@ -0,0 +1,112 @@ +package agent + +func init() { + registerEndpoint("/v1/acl/bootstrap", []string{"PUT"}, (*HTTPServer).ACLBootstrap) + registerEndpoint("/v1/acl/create", []string{"PUT"}, (*HTTPServer).ACLCreate) + registerEndpoint("/v1/acl/update", []string{"PUT"}, (*HTTPServer).ACLUpdate) + registerEndpoint("/v1/acl/destroy/", []string{"PUT"}, (*HTTPServer).ACLDestroy) + registerEndpoint("/v1/acl/info/", []string{"GET"}, (*HTTPServer).ACLGet) + registerEndpoint("/v1/acl/clone/", []string{"PUT"}, (*HTTPServer).ACLClone) + registerEndpoint("/v1/acl/list", []string{"GET"}, (*HTTPServer).ACLList) + registerEndpoint("/v1/acl/login", []string{"POST"}, (*HTTPServer).ACLLogin) + registerEndpoint("/v1/acl/logout", []string{"POST"}, (*HTTPServer).ACLLogout) + registerEndpoint("/v1/acl/replication", []string{"GET"}, (*HTTPServer).ACLReplicationStatus) + registerEndpoint("/v1/acl/policies", []string{"GET"}, (*HTTPServer).ACLPolicyList) + registerEndpoint("/v1/acl/policy", []string{"PUT"}, (*HTTPServer).ACLPolicyCreate) + registerEndpoint("/v1/acl/policy/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).ACLPolicyCRUD) + registerEndpoint("/v1/acl/roles", []string{"GET"}, (*HTTPServer).ACLRoleList) + registerEndpoint("/v1/acl/role", []string{"PUT"}, (*HTTPServer).ACLRoleCreate) + registerEndpoint("/v1/acl/role/name/", []string{"GET"}, (*HTTPServer).ACLRoleReadByName) + registerEndpoint("/v1/acl/role/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).ACLRoleCRUD) + registerEndpoint("/v1/acl/binding-rules", []string{"GET"}, (*HTTPServer).ACLBindingRuleList) + registerEndpoint("/v1/acl/binding-rule", []string{"PUT"}, (*HTTPServer).ACLBindingRuleCreate) + registerEndpoint("/v1/acl/binding-rule/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).ACLBindingRuleCRUD) + registerEndpoint("/v1/acl/auth-methods", []string{"GET"}, (*HTTPServer).ACLAuthMethodList) + registerEndpoint("/v1/acl/auth-method", []string{"PUT"}, (*HTTPServer).ACLAuthMethodCreate) + registerEndpoint("/v1/acl/auth-method/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).ACLAuthMethodCRUD) + registerEndpoint("/v1/acl/rules/translate", []string{"POST"}, (*HTTPServer).ACLRulesTranslate) + registerEndpoint("/v1/acl/rules/translate/", []string{"GET"}, (*HTTPServer).ACLRulesTranslateLegacyToken) + registerEndpoint("/v1/acl/tokens", []string{"GET"}, (*HTTPServer).ACLTokenList) + registerEndpoint("/v1/acl/token", []string{"PUT"}, (*HTTPServer).ACLTokenCreate) + registerEndpoint("/v1/acl/token/self", []string{"GET"}, (*HTTPServer).ACLTokenSelf) + registerEndpoint("/v1/acl/token/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).ACLTokenCRUD) + registerEndpoint("/v1/agent/token/", []string{"PUT"}, (*HTTPServer).AgentToken) + registerEndpoint("/v1/agent/self", []string{"GET"}, (*HTTPServer).AgentSelf) + registerEndpoint("/v1/agent/host", []string{"GET"}, (*HTTPServer).AgentHost) + registerEndpoint("/v1/agent/maintenance", []string{"PUT"}, (*HTTPServer).AgentNodeMaintenance) + registerEndpoint("/v1/agent/reload", []string{"PUT"}, (*HTTPServer).AgentReload) + registerEndpoint("/v1/agent/monitor", []string{"GET"}, (*HTTPServer).AgentMonitor) + registerEndpoint("/v1/agent/metrics", []string{"GET"}, (*HTTPServer).AgentMetrics) + registerEndpoint("/v1/agent/services", []string{"GET"}, (*HTTPServer).AgentServices) + registerEndpoint("/v1/agent/service/", []string{"GET"}, (*HTTPServer).AgentService) + registerEndpoint("/v1/agent/checks", []string{"GET"}, (*HTTPServer).AgentChecks) + registerEndpoint("/v1/agent/members", []string{"GET"}, (*HTTPServer).AgentMembers) + registerEndpoint("/v1/agent/join/", []string{"PUT"}, (*HTTPServer).AgentJoin) + registerEndpoint("/v1/agent/leave", []string{"PUT"}, (*HTTPServer).AgentLeave) + registerEndpoint("/v1/agent/force-leave/", []string{"PUT"}, (*HTTPServer).AgentForceLeave) + registerEndpoint("/v1/agent/health/service/id/", []string{"GET"}, (*HTTPServer).AgentHealthServiceByID) + registerEndpoint("/v1/agent/health/service/name/", []string{"GET"}, (*HTTPServer).AgentHealthServiceByName) + registerEndpoint("/v1/agent/check/register", []string{"PUT"}, (*HTTPServer).AgentRegisterCheck) + registerEndpoint("/v1/agent/check/deregister/", []string{"PUT"}, (*HTTPServer).AgentDeregisterCheck) + registerEndpoint("/v1/agent/check/pass/", []string{"PUT"}, (*HTTPServer).AgentCheckPass) + registerEndpoint("/v1/agent/check/warn/", []string{"PUT"}, (*HTTPServer).AgentCheckWarn) + registerEndpoint("/v1/agent/check/fail/", []string{"PUT"}, (*HTTPServer).AgentCheckFail) + registerEndpoint("/v1/agent/check/update/", []string{"PUT"}, (*HTTPServer).AgentCheckUpdate) + registerEndpoint("/v1/agent/connect/authorize", []string{"POST"}, (*HTTPServer).AgentConnectAuthorize) + registerEndpoint("/v1/agent/connect/ca/roots", []string{"GET"}, (*HTTPServer).AgentConnectCARoots) + registerEndpoint("/v1/agent/connect/ca/leaf/", []string{"GET"}, (*HTTPServer).AgentConnectCALeafCert) + registerEndpoint("/v1/agent/service/register", []string{"PUT"}, (*HTTPServer).AgentRegisterService) + registerEndpoint("/v1/agent/service/deregister/", []string{"PUT"}, (*HTTPServer).AgentDeregisterService) + registerEndpoint("/v1/agent/service/maintenance/", []string{"PUT"}, (*HTTPServer).AgentServiceMaintenance) + registerEndpoint("/v1/catalog/register", []string{"PUT"}, (*HTTPServer).CatalogRegister) + registerEndpoint("/v1/catalog/connect/", []string{"GET"}, (*HTTPServer).CatalogConnectServiceNodes) + registerEndpoint("/v1/catalog/deregister", []string{"PUT"}, (*HTTPServer).CatalogDeregister) + registerEndpoint("/v1/catalog/datacenters", []string{"GET"}, (*HTTPServer).CatalogDatacenters) + registerEndpoint("/v1/catalog/nodes", []string{"GET"}, (*HTTPServer).CatalogNodes) + registerEndpoint("/v1/catalog/services", []string{"GET"}, (*HTTPServer).CatalogServices) + registerEndpoint("/v1/catalog/service/", []string{"GET"}, (*HTTPServer).CatalogServiceNodes) + registerEndpoint("/v1/catalog/node/", []string{"GET"}, (*HTTPServer).CatalogNodeServices) + registerEndpoint("/v1/config/", []string{"GET", "DELETE"}, (*HTTPServer).Config) + registerEndpoint("/v1/config", []string{"PUT"}, (*HTTPServer).ConfigApply) + registerEndpoint("/v1/connect/ca/configuration", []string{"GET", "PUT"}, (*HTTPServer).ConnectCAConfiguration) + registerEndpoint("/v1/connect/ca/roots", []string{"GET"}, (*HTTPServer).ConnectCARoots) + registerEndpoint("/v1/connect/intentions", []string{"GET", "POST"}, (*HTTPServer).IntentionEndpoint) + registerEndpoint("/v1/connect/intentions/match", []string{"GET"}, (*HTTPServer).IntentionMatch) + registerEndpoint("/v1/connect/intentions/check", []string{"GET"}, (*HTTPServer).IntentionCheck) + registerEndpoint("/v1/connect/intentions/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).IntentionSpecific) + registerEndpoint("/v1/coordinate/datacenters", []string{"GET"}, (*HTTPServer).CoordinateDatacenters) + registerEndpoint("/v1/coordinate/nodes", []string{"GET"}, (*HTTPServer).CoordinateNodes) + registerEndpoint("/v1/coordinate/node/", []string{"GET"}, (*HTTPServer).CoordinateNode) + registerEndpoint("/v1/coordinate/update", []string{"PUT"}, (*HTTPServer).CoordinateUpdate) + registerEndpoint("/v1/discovery-chain/", []string{"GET", "POST"}, (*HTTPServer).DiscoveryChainRead) + registerEndpoint("/v1/event/fire/", []string{"PUT"}, (*HTTPServer).EventFire) + registerEndpoint("/v1/event/list", []string{"GET"}, (*HTTPServer).EventList) + registerEndpoint("/v1/health/node/", []string{"GET"}, (*HTTPServer).HealthNodeChecks) + registerEndpoint("/v1/health/checks/", []string{"GET"}, (*HTTPServer).HealthServiceChecks) + registerEndpoint("/v1/health/state/", []string{"GET"}, (*HTTPServer).HealthChecksInState) + registerEndpoint("/v1/health/service/", []string{"GET"}, (*HTTPServer).HealthServiceNodes) + registerEndpoint("/v1/health/connect/", []string{"GET"}, (*HTTPServer).HealthConnectServiceNodes) + registerEndpoint("/v1/internal/ui/nodes", []string{"GET"}, (*HTTPServer).UINodes) + registerEndpoint("/v1/internal/ui/node/", []string{"GET"}, (*HTTPServer).UINodeInfo) + registerEndpoint("/v1/internal/ui/services", []string{"GET"}, (*HTTPServer).UIServices) + registerEndpoint("/v1/kv/", []string{"GET", "PUT", "DELETE"}, (*HTTPServer).KVSEndpoint) + registerEndpoint("/v1/operator/raft/configuration", []string{"GET"}, (*HTTPServer).OperatorRaftConfiguration) + registerEndpoint("/v1/operator/raft/peer", []string{"DELETE"}, (*HTTPServer).OperatorRaftPeer) + registerEndpoint("/v1/operator/keyring", []string{"GET", "POST", "PUT", "DELETE"}, (*HTTPServer).OperatorKeyringEndpoint) + registerEndpoint("/v1/operator/autopilot/configuration", []string{"GET", "PUT"}, (*HTTPServer).OperatorAutopilotConfiguration) + registerEndpoint("/v1/operator/autopilot/health", []string{"GET"}, (*HTTPServer).OperatorServerHealth) + registerEndpoint("/v1/query", []string{"GET", "POST"}, (*HTTPServer).PreparedQueryGeneral) + // specific prepared query endpoints have more complex rules for allowed methods, so + // the prefix is registered with no methods. + registerEndpoint("/v1/query/", []string{}, (*HTTPServer).PreparedQuerySpecific) + registerEndpoint("/v1/session/create", []string{"PUT"}, (*HTTPServer).SessionCreate) + registerEndpoint("/v1/session/destroy/", []string{"PUT"}, (*HTTPServer).SessionDestroy) + registerEndpoint("/v1/session/renew/", []string{"PUT"}, (*HTTPServer).SessionRenew) + registerEndpoint("/v1/session/info/", []string{"GET"}, (*HTTPServer).SessionGet) + registerEndpoint("/v1/session/node/", []string{"GET"}, (*HTTPServer).SessionsForNode) + registerEndpoint("/v1/session/list", []string{"GET"}, (*HTTPServer).SessionList) + registerEndpoint("/v1/status/leader", []string{"GET"}, (*HTTPServer).StatusLeader) + registerEndpoint("/v1/status/peers", []string{"GET"}, (*HTTPServer).StatusPeers) + registerEndpoint("/v1/snapshot", []string{"GET", "PUT"}, (*HTTPServer).Snapshot) + registerEndpoint("/v1/txn", []string{"PUT"}, (*HTTPServer).Txn) +} diff --git a/agent/structs/acl.go b/agent/structs/acl.go index c0f3f23247..801277a0e1 100644 --- a/agent/structs/acl.go +++ b/agent/structs/acl.go @@ -86,22 +86,6 @@ session_prefix "" { ACLTokenAnonymousID = "00000000-0000-0000-0000-000000000002" ACLReservedPrefix = "00000000-0000-0000-0000-0000000000" - - // aclPolicyTemplateServiceIdentity is the template used for synthesizing - // policies for service identities. - aclPolicyTemplateServiceIdentity = ` -service "%s" { - policy = "write" -} -service "%s-sidecar-proxy" { - policy = "write" -} -service_prefix "" { - policy = "read" -} -node_prefix "" { - policy = "read" -}` ) func ACLIDReserved(id string) bool { @@ -134,6 +118,7 @@ type ACLIdentity interface { EmbeddedPolicy() *ACLPolicy ServiceIdentityList() []*ACLServiceIdentity IsExpired(asOf time.Time) bool + EnterpriseMetadata() *EnterpriseMeta } type ACLTokenPolicyLink struct { @@ -181,10 +166,11 @@ func (s *ACLServiceIdentity) EstimateSize() int { return size } -func (s *ACLServiceIdentity) SyntheticPolicy() *ACLPolicy { +func (s *ACLServiceIdentity) SyntheticPolicy(entMeta *EnterpriseMeta) *ACLPolicy { // Given that we validate this string name before persisting, we do not // have to escape it before doing the following interpolation. - rules := fmt.Sprintf(aclPolicyTemplateServiceIdentity, s.ServiceName, s.ServiceName) + // TODO (namespaces) include namespace + rules := aclServiceIdentityRules(s.ServiceName, entMeta) hasher := fnv.New128a() hashID := fmt.Sprintf("%x", hasher.Sum([]byte(rules))) @@ -268,6 +254,9 @@ type ACLToken struct { // unnecessary calls to the authoritative DC Hash []byte + // Embedded Enterprise Metadata + EnterpriseMeta `mapstructure:",squash"` + // Embedded Raft Metadata RaftIndex } @@ -390,6 +379,10 @@ func (t *ACLToken) EmbeddedPolicy() *ACLPolicy { return policy } +func (t *ACLToken) EnterpriseMetadata() *EnterpriseMeta { + return &t.EnterpriseMeta +} + func (t *ACLToken) SetHash(force bool) []byte { if force || t.Hash == nil { // Initialize a 256bit Blake2 hash (32 bytes) @@ -429,6 +422,8 @@ func (t *ACLToken) SetHash(force bool) []byte { srvid.AddToHash(hash) } + t.EnterpriseMeta.addToHash(hash) + // Finalize the hash hashVal := hash.Sum(nil) @@ -450,7 +445,7 @@ func (t *ACLToken) EstimateSize() int { for _, srvid := range t.ServiceIdentities { size += srvid.EstimateSize() } - return size + return size + t.EnterpriseMeta.estimateSize() } // ACLTokens is a slice of ACLTokens. @@ -470,6 +465,7 @@ type ACLTokenListStub struct { CreateIndex uint64 ModifyIndex uint64 Legacy bool `json:",omitempty"` + EnterpriseMeta } type ACLTokenListStubs []*ACLTokenListStub @@ -489,6 +485,7 @@ func (token *ACLToken) Stub() *ACLTokenListStub { CreateIndex: token.CreateIndex, ModifyIndex: token.ModifyIndex, Legacy: token.Rules != "", + EnterpriseMeta: token.EnterpriseMeta, } } @@ -536,6 +533,9 @@ type ACLPolicy struct { // unnecessary calls to the authoritative DC Hash []byte + // Embedded Enterprise ACL Metadata + EnterpriseMeta `mapstructure:",squash"` + // Embedded Raft Metadata RaftIndex `hash:"ignore"` } @@ -554,17 +554,19 @@ type ACLPolicyListStub struct { Hash []byte CreateIndex uint64 ModifyIndex uint64 + EnterpriseMeta } func (p *ACLPolicy) Stub() *ACLPolicyListStub { return &ACLPolicyListStub{ - ID: p.ID, - Name: p.Name, - Description: p.Description, - Datacenters: p.Datacenters, - Hash: p.Hash, - CreateIndex: p.CreateIndex, - ModifyIndex: p.ModifyIndex, + ID: p.ID, + Name: p.Name, + Description: p.Description, + Datacenters: p.Datacenters, + Hash: p.Hash, + CreateIndex: p.CreateIndex, + ModifyIndex: p.ModifyIndex, + EnterpriseMeta: p.EnterpriseMeta, } } @@ -595,6 +597,8 @@ func (p *ACLPolicy) SetHash(force bool) []byte { hash.Write([]byte(dc)) } + p.EnterpriseMeta.addToHash(hash) + // Finalize the hash hashVal := hash.Sum(nil) @@ -614,7 +618,7 @@ func (p *ACLPolicy) EstimateSize() int { size += len(dc) } - return size + return size + p.EnterpriseMeta.estimateSize() } // HashKey returns a consistent hash for a set of policies. @@ -657,7 +661,7 @@ func (policies ACLPolicies) resolveWithCache(cache *ACLCaches, entConf *acl.Ente continue } - p, err := acl.NewPolicyFromSource(policy.ID, policy.ModifyIndex, policy.Rules, policy.Syntax, entConf) + p, err := acl.NewPolicyFromSource(policy.ID, policy.ModifyIndex, policy.Rules, policy.Syntax, entConf, policy.EnterprisePolicyMeta()) if err != nil { return nil, fmt.Errorf("failed to parse %q: %v", policy.Name, err) } @@ -758,6 +762,9 @@ type ACLRole struct { // unnecessary calls to the authoritative DC Hash []byte + // Embedded Enterprise ACL metadata + EnterpriseMeta `mapstructure:",squash"` + // Embedded Raft Metadata RaftIndex `hash:"ignore"` } @@ -806,6 +813,8 @@ func (r *ACLRole) SetHash(force bool) []byte { srvid.AddToHash(hash) } + r.EnterpriseMeta.addToHash(hash) + // Finalize the hash hashVal := hash.Sum(nil) @@ -828,7 +837,7 @@ func (r *ACLRole) EstimateSize() int { size += srvid.EstimateSize() } - return size + return size + r.EnterpriseMeta.estimateSize() } const ( @@ -888,6 +897,9 @@ type ACLBindingRule struct { // upon the BindType. BindName string + // Embedded Enterprise ACL metadata + EnterpriseMeta `mapstructure:",squash"` + // Embedded Raft Metadata RaftIndex `hash:"ignore"` } @@ -911,15 +923,17 @@ type ACLAuthMethodListStub struct { Type string CreateIndex uint64 ModifyIndex uint64 + EnterpriseMeta } func (p *ACLAuthMethod) Stub() *ACLAuthMethodListStub { return &ACLAuthMethodListStub{ - Name: p.Name, - Description: p.Description, - Type: p.Type, - CreateIndex: p.CreateIndex, - ModifyIndex: p.ModifyIndex, + Name: p.Name, + Description: p.Description, + Type: p.Type, + CreateIndex: p.CreateIndex, + ModifyIndex: p.ModifyIndex, + EnterpriseMeta: p.EnterpriseMeta, } } @@ -957,6 +971,9 @@ type ACLAuthMethod struct { // maps). Config map[string]interface{} + // Embedded Enterprise ACL Meta + EnterpriseMeta `mapstructure:",squash"` + // Embedded Raft Metadata RaftIndex `hash:"ignore"` } @@ -1017,6 +1034,7 @@ type ACLTokenGetRequest struct { TokenID string // id used for the token lookup TokenIDType ACLTokenIDType // The Type of ID used to lookup the token Datacenter string // The datacenter to perform the request within + EnterpriseMeta QueryOptions } @@ -1028,6 +1046,7 @@ func (r *ACLTokenGetRequest) RequestDatacenter() string { type ACLTokenDeleteRequest struct { TokenID string // ID of the token to delete Datacenter string // The datacenter to perform the request within + EnterpriseMeta WriteRequest } @@ -1043,6 +1062,7 @@ type ACLTokenListRequest struct { Role string // Role filter AuthMethod string // Auth Method filter Datacenter string // The datacenter to perform the request within + EnterpriseMeta QueryOptions } @@ -1063,6 +1083,8 @@ type ACLTokenListResponse struct { type ACLTokenBatchGetRequest struct { AccessorIDs []string // List of accessor ids to fetch Datacenter string // The datacenter to perform the request within + // TODO (namespaces) should this use enterprise meta? - it would be hard to + // update in a backwards compatible manner an accessor ids should be unique QueryOptions } @@ -1089,6 +1111,7 @@ type ACLTokenBatchSetRequest struct { // multiple tokens need to be removed from the local DCs state. type ACLTokenBatchDeleteRequest struct { TokenIDs []string // Tokens to delete + // TODO (namespaces) should we update with ent meta? } // ACLTokenBootstrapRequest is used only at the Raft layer @@ -1113,6 +1136,7 @@ type ACLTokenResponse struct { type ACLTokenBatchResponse struct { Tokens []*ACLToken Redacted bool // whether the token secrets were redacted. + Removed bool // whether any tokens were completely removed QueryMeta } @@ -1131,6 +1155,7 @@ func (r *ACLPolicySetRequest) RequestDatacenter() string { type ACLPolicyDeleteRequest struct { PolicyID string // The id of the policy to delete Datacenter string // The datacenter to perform the request within + EnterpriseMeta WriteRequest } @@ -1142,6 +1167,7 @@ func (r *ACLPolicyDeleteRequest) RequestDatacenter() string { type ACLPolicyGetRequest struct { PolicyID string // id used for the policy lookup Datacenter string // The datacenter to perform the request within + EnterpriseMeta QueryOptions } @@ -1152,6 +1178,7 @@ func (r *ACLPolicyGetRequest) RequestDatacenter() string { // ACLPolicyListRequest is used at the RPC layer to request a listing of policies type ACLPolicyListRequest struct { Datacenter string // The datacenter to perform the request within + EnterpriseMeta QueryOptions } @@ -1227,6 +1254,7 @@ func (r *ACLRoleSetRequest) RequestDatacenter() string { type ACLRoleDeleteRequest struct { RoleID string // id of the role to delete Datacenter string // The datacenter to perform the request within + EnterpriseMeta WriteRequest } @@ -1239,6 +1267,7 @@ type ACLRoleGetRequest struct { RoleID string // id used for the role lookup (one of RoleID or RoleName is allowed) RoleName string // name used for the role lookup (one of RoleID or RoleName is allowed) Datacenter string // The datacenter to perform the request within + EnterpriseMeta QueryOptions } @@ -1250,6 +1279,7 @@ func (r *ACLRoleGetRequest) RequestDatacenter() string { type ACLRoleListRequest struct { Policy string // Policy filter Datacenter string // The datacenter to perform the request within + EnterpriseMeta QueryOptions } @@ -1317,6 +1347,7 @@ func (r *ACLBindingRuleSetRequest) RequestDatacenter() string { type ACLBindingRuleDeleteRequest struct { BindingRuleID string // id of the rule to delete Datacenter string // The datacenter to perform the request within + EnterpriseMeta WriteRequest } @@ -1328,6 +1359,7 @@ func (r *ACLBindingRuleDeleteRequest) RequestDatacenter() string { type ACLBindingRuleGetRequest struct { BindingRuleID string // id used for the rule lookup Datacenter string // The datacenter to perform the request within + EnterpriseMeta QueryOptions } @@ -1339,6 +1371,7 @@ func (r *ACLBindingRuleGetRequest) RequestDatacenter() string { type ACLBindingRuleListRequest struct { AuthMethod string // optional filter Datacenter string // The datacenter to perform the request within + EnterpriseMeta QueryOptions } @@ -1384,6 +1417,7 @@ func (r *ACLAuthMethodSetRequest) RequestDatacenter() string { type ACLAuthMethodDeleteRequest struct { AuthMethodName string // name of the auth method to delete Datacenter string // The datacenter to perform the request within + EnterpriseMeta WriteRequest } @@ -1395,6 +1429,7 @@ func (r *ACLAuthMethodDeleteRequest) RequestDatacenter() string { type ACLAuthMethodGetRequest struct { AuthMethodName string // name used for the auth method lookup Datacenter string // The datacenter to perform the request within + EnterpriseMeta QueryOptions } @@ -1405,6 +1440,7 @@ func (r *ACLAuthMethodGetRequest) RequestDatacenter() string { // ACLAuthMethodListRequest is used at the RPC layer to request a listing of auth methods type ACLAuthMethodListRequest struct { Datacenter string // The datacenter to perform the request within + EnterpriseMeta QueryOptions } @@ -1433,12 +1469,19 @@ type ACLAuthMethodBatchSetRequest struct { // multiple auth method deletions type ACLAuthMethodBatchDeleteRequest struct { AuthMethodNames []string + // While it may seem odd that AuthMethodNames is associated with a single + // EnterpriseMeta, it is okay as this struct is only ever used to + // delete a single entry. This is because AuthMethods unlike tokens, policies + // and roles are not replicated between datacenters and therefore never + // batch applied. + EnterpriseMeta } type ACLLoginParams struct { AuthMethod string BearerToken string Meta map[string]string `json:",omitempty"` + EnterpriseMeta } type ACLLoginRequest struct { @@ -1453,6 +1496,8 @@ func (r *ACLLoginRequest) RequestDatacenter() string { type ACLLogoutRequest struct { Datacenter string // The datacenter to perform the request within + // TODO (namespaces) do we need the ent meta here? tokens are again + // unique across namespaces so its likely we don't need it. WriteRequest } diff --git a/agent/structs/acl_oss.go b/agent/structs/acl_oss.go index f5841c63d1..30173e97b8 100644 --- a/agent/structs/acl_oss.go +++ b/agent/structs/acl_oss.go @@ -2,6 +2,36 @@ package structs +import ( + "fmt" + + "github.com/hashicorp/consul/acl" +) + const ( EnterpriseACLPolicyGlobalManagement = "" + + // aclPolicyTemplateServiceIdentity is the template used for synthesizing + // policies for service identities. + aclPolicyTemplateServiceIdentity = ` +service "%[1]s" { + policy = "write" +} +service "%[1]s-sidecar-proxy" { + policy = "write" +} +service_prefix "" { + policy = "read" +} +node_prefix "" { + policy = "read" +}` ) + +func aclServiceIdentityRules(svc string, _ *EnterpriseMeta) string { + return fmt.Sprintf(aclPolicyTemplateServiceIdentity, svc) +} + +func (p *ACLPolicy) EnterprisePolicyMeta() *acl.EnterprisePolicyMeta { + return nil +} diff --git a/agent/structs/acl_test.go b/agent/structs/acl_test.go index b15a919f9d..fc18de44dc 100644 --- a/agent/structs/acl_test.go +++ b/agent/structs/acl_test.go @@ -192,7 +192,7 @@ node_prefix "" { Rules: test.expectRules, } - got := svcid.SyntheticPolicy() + got := svcid.SyntheticPolicy(nil) require.NotEmpty(t, got.ID) require.True(t, strings.HasPrefix(got.Name, "synthetic-policy-")) // strip irrelevant fields before equality diff --git a/agent/structs/config_entry_discoverychain_test.go b/agent/structs/config_entry_discoverychain_test.go index 1f140a7178..9ae4aff3c5 100644 --- a/agent/structs/config_entry_discoverychain_test.go +++ b/agent/structs/config_entry_discoverychain_test.go @@ -24,7 +24,7 @@ func TestConfigEntries_ListRelatedServices_AndACLs(t *testing.T) { buf.WriteString(fmt.Sprintf("service %q { policy = %q }\n", s, "write")) } - policy, err := acl.NewPolicyFromSource("", 0, buf.String(), acl.SyntaxCurrent, nil) + policy, err := acl.NewPolicyFromSource("", 0, buf.String(), acl.SyntaxCurrent, nil, nil) require.NoError(t, err) authorizer, err := acl.NewPolicyAuthorizerWithDefaults(acl.DenyAll(), []*acl.Policy{policy}, nil) diff --git a/agent/structs/structs.go b/agent/structs/structs.go index b303158cbe..833a2940bd 100644 --- a/agent/structs/structs.go +++ b/agent/structs/structs.go @@ -1808,6 +1808,35 @@ func Encode(t MessageType, msg interface{}) ([]byte, error) { return buf.Bytes(), err } +type ProtoMarshaller interface { + Size() int + MarshalTo([]byte) (int, error) + Unmarshal([]byte) error + ProtoMessage() +} + +func EncodeProtoInterface(t MessageType, message interface{}) ([]byte, error) { + if marshaller, ok := message.(ProtoMarshaller); ok { + return EncodeProto(t, marshaller) + } + + return nil, fmt.Errorf("message does not implement the ProtoMarshaller interface: %T", message) +} + +func EncodeProto(t MessageType, message ProtoMarshaller) ([]byte, error) { + data := make([]byte, message.Size()+1) + data[0] = uint8(t) + if _, err := message.MarshalTo(data[1:]); err != nil { + return nil, err + } + return data, nil +} + +func DecodeProto(buf []byte, out ProtoMarshaller) error { + // Note that this assumes the leading byte indicating the type as already been stripped off. + return out.Unmarshal(buf) +} + // CompoundResponse is an interface for gathering multiple responses. It is // used in cross-datacenter RPC calls where more than 1 datacenter is // expected to reply. diff --git a/agent/structs/structs_oss.go b/agent/structs/structs_oss.go new file mode 100644 index 0000000000..72d6831b62 --- /dev/null +++ b/agent/structs/structs_oss.go @@ -0,0 +1,36 @@ +// +build !consulent + +package structs + +import ( + "hash" + + "github.com/hashicorp/consul/acl" +) + +// EnterpriseMeta stub +type EnterpriseMeta struct{} + +func (m *EnterpriseMeta) estimateSize() int { + return 0 +} + +func (m *EnterpriseMeta) addToHash(hasher hash.Hash) { + // do nothing +} + +// ReplicationEnterpriseMeta stub +func ReplicationEnterpriseMeta() *EnterpriseMeta { + return nil +} + +// DefaultEnterpriseMeta stub +func DefaultEnterpriseMeta() *EnterpriseMeta { + return nil +} + +// InitDefault stub +func (m *EnterpriseMeta) InitDefault() {} + +// FillAuthzContext stub +func (m *EnterpriseMeta) FillAuthzContext(*acl.EnterpriseAuthorizerContext) {} diff --git a/agent/xds/server_test.go b/agent/xds/server_test.go index 11ac1552cd..ecf8c31232 100644 --- a/agent/xds/server_test.go +++ b/agent/xds/server_test.go @@ -448,7 +448,7 @@ func TestServer_StreamAggregatedResources_ACLEnforcement(t *testing.T) { // Ensure the correct token was passed require.Equal(t, tt.token, id) // Parse the ACL and enforce it - policy, err := acl.NewPolicyFromSource("", 0, tt.acl, acl.SyntaxLegacy, nil) + policy, err := acl.NewPolicyFromSource("", 0, tt.acl, acl.SyntaxLegacy, nil, nil) require.NoError(t, err) return acl.NewPolicyAuthorizerWithDefaults(acl.RootAuthorizer("deny"), []*acl.Policy{policy}, nil) } @@ -508,7 +508,7 @@ func TestServer_StreamAggregatedResources_ACLTokenDeleted_StreamTerminatedDuring aclRules := `service "web" { policy = "write" }` token := "service-write-on-web" - policy, err := acl.NewPolicyFromSource("", 0, aclRules, acl.SyntaxLegacy, nil) + policy, err := acl.NewPolicyFromSource("", 0, aclRules, acl.SyntaxLegacy, nil, nil) require.NoError(t, err) var validToken atomic.Value @@ -599,7 +599,7 @@ func TestServer_StreamAggregatedResources_ACLTokenDeleted_StreamTerminatedInBack aclRules := `service "web" { policy = "write" }` token := "service-write-on-web" - policy, err := acl.NewPolicyFromSource("", 0, aclRules, acl.SyntaxLegacy, nil) + policy, err := acl.NewPolicyFromSource("", 0, aclRules, acl.SyntaxLegacy, nil, nil) require.NoError(t, err) var validToken atomic.Value