f9a43a1e2d
* 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
130 lines
3.2 KiB
Go
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
|
|
}
|