1package consul
2
3import (
4	"fmt"
5
6	"github.com/hashicorp/consul/agent/consul/authmethod"
7	"github.com/hashicorp/consul/agent/structs"
8	"github.com/hashicorp/go-bexpr"
9
10	// register these as a builtin auth method
11	_ "github.com/hashicorp/consul/agent/consul/authmethod/kubeauth"
12	_ "github.com/hashicorp/consul/agent/consul/authmethod/ssoauth"
13)
14
15type authMethodValidatorEntry struct {
16	Validator   authmethod.Validator
17	ModifyIndex uint64 // the raft index when this last changed
18}
19
20// loadAuthMethodValidator returns an authmethod.Validator for the given auth
21// method configuration. If the cache is up to date as-of the provided index
22// then the cached version is returned, otherwise a new validator is created
23// and cached.
24func (s *Server) loadAuthMethodValidator(idx uint64, method *structs.ACLAuthMethod) (authmethod.Validator, error) {
25	if prevIdx, v, ok := s.aclAuthMethodValidators.GetValidator(method); ok && idx <= prevIdx {
26		return v, nil
27	}
28
29	v, err := authmethod.NewValidator(s.logger, method)
30	if err != nil {
31		return nil, fmt.Errorf("auth method validator for %q could not be initialized: %v", method.Name, err)
32	}
33
34	v = s.aclAuthMethodValidators.PutValidatorIfNewer(method, v, idx)
35
36	return v, nil
37}
38
39type aclBindings struct {
40	roles             []structs.ACLTokenRoleLink
41	serviceIdentities []*structs.ACLServiceIdentity
42	nodeIdentities    []*structs.ACLNodeIdentity
43}
44
45// evaluateRoleBindings evaluates all current binding rules associated with the
46// given auth method against the verified data returned from the authentication
47// process.
48//
49// A list of role links and service identities are returned.
50func (s *Server) evaluateRoleBindings(
51	validator authmethod.Validator,
52	verifiedIdentity *authmethod.Identity,
53	methodMeta *structs.EnterpriseMeta,
54	targetMeta *structs.EnterpriseMeta,
55) (*aclBindings, error) {
56	// Only fetch rules that are relevant for this method.
57	_, rules, err := s.fsm.State().ACLBindingRuleList(nil, validator.Name(), methodMeta)
58	if err != nil {
59		return nil, err
60	} else if len(rules) == 0 {
61		return nil, nil
62	}
63
64	// Find all binding rules that match the provided fields.
65	var matchingRules []*structs.ACLBindingRule
66	for _, rule := range rules {
67		if doesSelectorMatch(rule.Selector, verifiedIdentity.SelectableFields) {
68			matchingRules = append(matchingRules, rule)
69		}
70	}
71	if len(matchingRules) == 0 {
72		return nil, nil
73	}
74
75	// For all matching rules compute the attributes of a token.
76	var bindings aclBindings
77	for _, rule := range matchingRules {
78		bindName, valid, err := computeBindingRuleBindName(rule.BindType, rule.BindName, verifiedIdentity.ProjectedVars)
79		if err != nil {
80			return nil, fmt.Errorf("cannot compute %q bind name for bind target: %v", rule.BindType, err)
81		} else if !valid {
82			return nil, fmt.Errorf("computed %q bind name for bind target is invalid: %q", rule.BindType, bindName)
83		}
84
85		switch rule.BindType {
86		case structs.BindingRuleBindTypeService:
87			bindings.serviceIdentities = append(bindings.serviceIdentities, &structs.ACLServiceIdentity{
88				ServiceName: bindName,
89			})
90
91		case structs.BindingRuleBindTypeNode:
92			bindings.nodeIdentities = append(bindings.nodeIdentities, &structs.ACLNodeIdentity{
93				NodeName:   bindName,
94				Datacenter: s.config.Datacenter,
95			})
96
97		case structs.BindingRuleBindTypeRole:
98			_, role, err := s.fsm.State().ACLRoleGetByName(nil, bindName, targetMeta)
99			if err != nil {
100				return nil, err
101			}
102
103			if role != nil {
104				bindings.roles = append(bindings.roles, structs.ACLTokenRoleLink{
105					ID: role.ID,
106				})
107			}
108
109		default:
110			// skip unknown bind type; don't grant privileges
111		}
112	}
113
114	return &bindings, nil
115}
116
117// doesSelectorMatch checks that a single selector matches the provided vars.
118func doesSelectorMatch(selector string, selectableVars interface{}) bool {
119	if selector == "" {
120		return true // catch-all
121	}
122
123	eval, err := bexpr.CreateEvaluatorForType(selector, nil, selectableVars)
124	if err != nil {
125		return false // fails to match if selector is invalid
126	}
127
128	result, err := eval.Evaluate(selectableVars)
129	if err != nil {
130		return false // fails to match if evaluation fails
131	}
132
133	return result
134}
135