open-consul/agent/consul/filter.go
Matt Keeler f9a43a1e2d
ACL Authorizer overhaul (#6620)
* ACL Authorizer overhaul

To account for upcoming features every Authorization function can now take an extra *acl.EnterpriseAuthorizerContext. These are unused in OSS and will always be nil.

Additionally the acl package has received some thorough refactoring to enable all of the extra Consul Enterprise specific authorizations including moving sentinel enforcement into the stubbed structs. The Authorizer funcs now return an acl.EnforcementDecision instead of a boolean. This improves the overall interface as it makes multiple Authorizers easily chainable as they now indicate whether they had an authoritative decision or should use some other defaults. A ChainedAuthorizer was added to handle this Authorizer enforcement chain and will never itself return a non-authoritative decision.

* Include stub for extra enterprise rules in the global management policy

* Allow for an upgrade of the global-management policy
2019-10-15 16:58:50 -04:00

130 lines
3.2 KiB
Go

package consul
import (
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/structs"
)
type dirEntFilter struct {
authorizer acl.Authorizer
ent structs.DirEntries
}
func (d *dirEntFilter) Len() int {
return len(d.ent)
}
func (d *dirEntFilter) Filter(i int) bool {
return d.authorizer.KeyRead(d.ent[i].Key, nil) != acl.Allow
}
func (d *dirEntFilter) Move(dst, src, span int) {
copy(d.ent[dst:dst+span], d.ent[src:src+span])
}
// FilterDirEnt is used to filter a list of directory entries
// by applying an ACL policy
func FilterDirEnt(authorizer acl.Authorizer, ent structs.DirEntries) structs.DirEntries {
df := dirEntFilter{authorizer: authorizer, ent: ent}
return ent[:FilterEntries(&df)]
}
type keyFilter struct {
authorizer acl.Authorizer
keys []string
}
func (k *keyFilter) Len() int {
return len(k.keys)
}
func (k *keyFilter) Filter(i int) bool {
// TODO (namespaces) use a real ent authz context here
return k.authorizer.KeyRead(k.keys[i], nil) != acl.Allow
}
func (k *keyFilter) Move(dst, src, span int) {
copy(k.keys[dst:dst+span], k.keys[src:src+span])
}
// FilterKeys is used to filter a list of keys by
// applying an ACL policy
func FilterKeys(authorizer acl.Authorizer, keys []string) []string {
kf := keyFilter{authorizer: authorizer, keys: keys}
return keys[:FilterEntries(&kf)]
}
type txnResultsFilter struct {
authorizer acl.Authorizer
results structs.TxnResults
}
func (t *txnResultsFilter) Len() int {
return len(t.results)
}
func (t *txnResultsFilter) Filter(i int) bool {
// TODO (namespaces) use a real ent authz context for most of these checks
result := t.results[i]
switch {
case result.KV != nil:
return t.authorizer.KeyRead(result.KV.Key, nil) != acl.Allow
case result.Node != nil:
return t.authorizer.NodeRead(result.Node.Node, nil) != acl.Allow
case result.Service != nil:
return t.authorizer.ServiceRead(result.Service.Service, nil) != acl.Allow
case result.Check != nil:
if result.Check.ServiceName != "" {
return t.authorizer.ServiceRead(result.Check.ServiceName, nil) != acl.Allow
}
return t.authorizer.NodeRead(result.Check.Node, nil) != acl.Allow
}
return false
}
func (t *txnResultsFilter) Move(dst, src, span int) {
copy(t.results[dst:dst+span], t.results[src:src+span])
}
// FilterTxnResults is used to filter a list of transaction results by
// applying an ACL policy.
func FilterTxnResults(authorizer acl.Authorizer, results structs.TxnResults) structs.TxnResults {
rf := txnResultsFilter{authorizer: authorizer, results: results}
return results[:FilterEntries(&rf)]
}
// Filter interface is used with FilterEntries to do an
// in-place filter of a slice.
type Filter interface {
Len() int
Filter(int) bool
Move(dst, src, span int)
}
// FilterEntries is used to do an inplace filter of
// a slice. This has cost proportional to the list length.
func FilterEntries(f Filter) int {
// Compact the list
dst := 0
src := 0
n := f.Len()
for dst < n {
for src < n && f.Filter(src) {
src++
}
if src == n {
break
}
end := src + 1
for end < n && !f.Filter(end) {
end++
}
span := end - src
if span > 0 {
f.Move(dst, src, span)
dst += span
src += span
}
}
// Return the size of the slice
return dst
}