2020-02-10 20:56:14 +00:00
|
|
|
// +build !ent
|
2017-09-07 23:56:15 +00:00
|
|
|
|
|
|
|
package nomad
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
memdb "github.com/hashicorp/go-memdb"
|
2017-10-12 01:05:27 +00:00
|
|
|
"github.com/hashicorp/nomad/acl"
|
2017-09-07 23:56:15 +00:00
|
|
|
"github.com/hashicorp/nomad/nomad/state"
|
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
|
|
)
|
|
|
|
|
2017-09-08 00:13:18 +00:00
|
|
|
var (
|
|
|
|
// allContexts are the available contexts which are searched to find matches
|
|
|
|
// for a given prefix
|
|
|
|
allContexts = ossContexts
|
|
|
|
)
|
|
|
|
|
2017-10-13 21:36:02 +00:00
|
|
|
// contextToIndex returns the index name to lookup in the state store.
|
|
|
|
func contextToIndex(ctx structs.Context) string {
|
|
|
|
return string(ctx)
|
|
|
|
}
|
|
|
|
|
2017-09-07 23:56:15 +00:00
|
|
|
// getEnterpriseMatch is a no-op in oss since there are no enterprise objects.
|
|
|
|
func getEnterpriseMatch(match interface{}) (id string, ok bool) {
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
|
|
|
// getEnterpriseResourceIter is used to retrieve an iterator over an enterprise
|
|
|
|
// only table.
|
2017-10-13 21:36:02 +00:00
|
|
|
func getEnterpriseResourceIter(context structs.Context, _ *acl.ACL, namespace, prefix string, ws memdb.WatchSet, state *state.StateStore) (memdb.ResultIterator, error) {
|
2017-09-07 23:56:15 +00:00
|
|
|
// If we have made it here then it is an error since we have exhausted all
|
|
|
|
// open source contexts.
|
2017-09-08 00:13:18 +00:00
|
|
|
return nil, fmt.Errorf("context must be one of %v or 'all' for all contexts; got %q", allContexts, context)
|
2017-09-07 23:56:15 +00:00
|
|
|
}
|
2017-10-12 01:05:27 +00:00
|
|
|
|
|
|
|
// anySearchPerms returns true if the provided ACL has access to any
|
|
|
|
// capabilities required for prefix searching. Returns true if aclObj is nil.
|
|
|
|
func anySearchPerms(aclObj *acl.ACL, namespace string, context structs.Context) bool {
|
|
|
|
if aclObj == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
nodeRead := aclObj.AllowNodeRead()
|
2020-10-21 04:16:25 +00:00
|
|
|
allowNS := aclObj.AllowNamespace(namespace)
|
2017-10-12 01:05:27 +00:00
|
|
|
jobRead := aclObj.AllowNsOp(namespace, acl.NamespaceCapabilityReadJob)
|
2020-03-17 21:32:39 +00:00
|
|
|
allowVolume := acl.NamespaceValidator(acl.NamespaceCapabilityCSIListVolume,
|
|
|
|
acl.NamespaceCapabilityCSIReadVolume,
|
|
|
|
acl.NamespaceCapabilityListJobs,
|
|
|
|
acl.NamespaceCapabilityReadJob)
|
|
|
|
volRead := allowVolume(aclObj, namespace)
|
|
|
|
|
2020-10-21 04:16:25 +00:00
|
|
|
if !nodeRead && !jobRead && !volRead && !allowNS {
|
2017-10-12 01:05:27 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reject requests that explicitly specify a disallowed context. This
|
|
|
|
// should give the user better feedback then simply filtering out all
|
|
|
|
// results and returning an empty list.
|
|
|
|
if !nodeRead && context == structs.Nodes {
|
|
|
|
return false
|
|
|
|
}
|
2020-10-21 04:16:25 +00:00
|
|
|
if !allowNS && context == structs.Namespaces {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-10-12 01:05:27 +00:00
|
|
|
if !jobRead {
|
|
|
|
switch context {
|
|
|
|
case structs.Allocs, structs.Deployments, structs.Evals, structs.Jobs:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2020-03-17 21:32:39 +00:00
|
|
|
if !volRead && context == structs.Volumes {
|
|
|
|
return false
|
|
|
|
}
|
2017-10-12 01:05:27 +00:00
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// searchContexts returns the contexts the aclObj is valid for. If aclObj is
|
|
|
|
// nil all contexts are returned.
|
|
|
|
func searchContexts(aclObj *acl.ACL, namespace string, context structs.Context) []structs.Context {
|
|
|
|
var all []structs.Context
|
|
|
|
|
|
|
|
switch context {
|
|
|
|
case structs.All:
|
|
|
|
all = make([]structs.Context, len(allContexts))
|
|
|
|
copy(all, allContexts)
|
|
|
|
default:
|
|
|
|
all = []structs.Context{context}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If ACLs aren't enabled return all contexts
|
|
|
|
if aclObj == nil {
|
|
|
|
return all
|
|
|
|
}
|
|
|
|
|
|
|
|
jobRead := aclObj.AllowNsOp(namespace, acl.NamespaceCapabilityReadJob)
|
2020-03-17 21:32:39 +00:00
|
|
|
allowVolume := acl.NamespaceValidator(acl.NamespaceCapabilityCSIListVolume,
|
|
|
|
acl.NamespaceCapabilityCSIReadVolume,
|
|
|
|
acl.NamespaceCapabilityListJobs,
|
|
|
|
acl.NamespaceCapabilityReadJob)
|
|
|
|
volRead := allowVolume(aclObj, namespace)
|
2017-10-12 01:05:27 +00:00
|
|
|
|
|
|
|
// Filter contexts down to those the ACL grants access to
|
|
|
|
available := make([]structs.Context, 0, len(all))
|
|
|
|
for _, c := range all {
|
|
|
|
switch c {
|
|
|
|
case structs.Allocs, structs.Jobs, structs.Evals, structs.Deployments:
|
|
|
|
if jobRead {
|
|
|
|
available = append(available, c)
|
|
|
|
}
|
2020-10-21 04:16:25 +00:00
|
|
|
case structs.Namespaces:
|
|
|
|
if aclObj.AllowNamespace(namespace) {
|
|
|
|
available = append(available, c)
|
|
|
|
}
|
2017-10-12 01:05:27 +00:00
|
|
|
case structs.Nodes:
|
|
|
|
if aclObj.AllowNodeRead() {
|
|
|
|
available = append(available, c)
|
|
|
|
}
|
2020-03-17 21:32:39 +00:00
|
|
|
case structs.Volumes:
|
|
|
|
if volRead {
|
|
|
|
available = append(available, c)
|
|
|
|
}
|
2017-10-12 01:05:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return available
|
|
|
|
}
|