Updates to the Txn API for namespaces (#7172)

* Updates to the Txn API for namespaces

* Update agent/consul/txn_endpoint.go

Co-Authored-By: R.B. Boyer <rb@hashicorp.com>

Co-authored-by: R.B. Boyer <public@richardboyer.net>
pull/7182/head
Matt Keeler 2020-01-30 13:12:26 -05:00 committed by GitHub
parent 74c277f5e1
commit 6855a778c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 34 additions and 16 deletions

View File

@ -40,20 +40,24 @@ func (t *txnResultsFilter) Len() int {
} }
func (t *txnResultsFilter) Filter(i int) bool { func (t *txnResultsFilter) Filter(i int) bool {
// TODO (namespaces) use a real ent authz context for most of these checks
result := t.results[i] result := t.results[i]
var authzContext acl.AuthorizerContext
switch { switch {
case result.KV != nil: case result.KV != nil:
return t.authorizer.KeyRead(result.KV.Key, nil) != acl.Allow result.KV.EnterpriseMeta.FillAuthzContext(&authzContext)
return t.authorizer.KeyRead(result.KV.Key, &authzContext) != acl.Allow
case result.Node != nil: case result.Node != nil:
return t.authorizer.NodeRead(result.Node.Node, nil) != acl.Allow structs.WildcardEnterpriseMeta().FillAuthzContext(&authzContext)
return t.authorizer.NodeRead(result.Node.Node, &authzContext) != acl.Allow
case result.Service != nil: case result.Service != nil:
return t.authorizer.ServiceRead(result.Service.Service, nil) != acl.Allow result.Service.EnterpriseMeta.FillAuthzContext(&authzContext)
return t.authorizer.ServiceRead(result.Service.Service, &authzContext) != acl.Allow
case result.Check != nil: case result.Check != nil:
result.Check.EnterpriseMeta.FillAuthzContext(&authzContext)
if result.Check.ServiceName != "" { if result.Check.ServiceName != "" {
return t.authorizer.ServiceRead(result.Check.ServiceName, nil) != acl.Allow return t.authorizer.ServiceRead(result.Check.ServiceName, &authzContext) != acl.Allow
} }
return t.authorizer.NodeRead(result.Check.Node, nil) != acl.Allow return t.authorizer.NodeRead(result.Check.Node, &authzContext) != acl.Allow
} }
return false return false
} }

View File

@ -68,6 +68,8 @@ func (t *Txn) preCheck(authorizer acl.Authorizer, ops structs.TxnOps) structs.Tx
} }
service := &op.Service.Service service := &op.Service.Service
// This is intentionally nil as we will authorize the request
// using vetServiceTxnOp next instead of doing it in servicePreApply
if err := servicePreApply(service, nil); err != nil { if err := servicePreApply(service, nil); err != nil {
errors = append(errors, &structs.TxnError{ errors = append(errors, &structs.TxnError{
OpIndex: i, OpIndex: i,

View File

@ -46,6 +46,10 @@ func (m *EnterpriseMeta) NamespaceOrDefault() string {
return "default" return "default"
} }
func EnterpriseMetaInitializer(_ string) EnterpriseMeta {
return emptyEnterpriseMeta
}
// ReplicationEnterpriseMeta stub // ReplicationEnterpriseMeta stub
func ReplicationEnterpriseMeta() *EnterpriseMeta { func ReplicationEnterpriseMeta() *EnterpriseMeta {
return &emptyEnterpriseMeta return &emptyEnterpriseMeta

View File

@ -125,10 +125,11 @@ func (s *HTTPServer) convertOps(resp http.ResponseWriter, req *http.Request) (st
KV: &structs.TxnKVOp{ KV: &structs.TxnKVOp{
Verb: verb, Verb: verb,
DirEnt: structs.DirEntry{ DirEnt: structs.DirEntry{
Key: in.KV.Key, Key: in.KV.Key,
Value: in.KV.Value, Value: in.KV.Value,
Flags: in.KV.Flags, Flags: in.KV.Flags,
Session: in.KV.Session, Session: in.KV.Session,
EnterpriseMeta: structs.EnterpriseMetaInitializer(in.KV.Namespace),
RaftIndex: structs.RaftIndex{ RaftIndex: structs.RaftIndex{
ModifyIndex: in.KV.Index, ModifyIndex: in.KV.Index,
}, },
@ -188,6 +189,7 @@ func (s *HTTPServer) convertOps(resp http.ResponseWriter, req *http.Request) (st
Warning: svc.Weights.Warning, Warning: svc.Weights.Warning,
}, },
EnableTagOverride: svc.EnableTagOverride, EnableTagOverride: svc.EnableTagOverride,
EnterpriseMeta: structs.EnterpriseMetaInitializer(svc.Namespace),
RaftIndex: structs.RaftIndex{ RaftIndex: structs.RaftIndex{
ModifyIndex: svc.ModifyIndex, ModifyIndex: svc.ModifyIndex,
}, },
@ -243,6 +245,7 @@ func (s *HTTPServer) convertOps(resp http.ResponseWriter, req *http.Request) (st
Timeout: timeout, Timeout: timeout,
DeregisterCriticalServiceAfter: deregisterCriticalServiceAfter, DeregisterCriticalServiceAfter: deregisterCriticalServiceAfter,
}, },
EnterpriseMeta: structs.EnterpriseMetaInitializer(check.Namespace),
RaftIndex: structs.RaftIndex{ RaftIndex: structs.RaftIndex{
ModifyIndex: check.ModifyIndex, ModifyIndex: check.ModifyIndex,
}, },

View File

@ -75,12 +75,13 @@ const (
// KVTxnOp defines a single operation inside a transaction. // KVTxnOp defines a single operation inside a transaction.
type KVTxnOp struct { type KVTxnOp struct {
Verb KVOp Verb KVOp
Key string Key string
Value []byte Value []byte
Flags uint64 Flags uint64
Index uint64 Index uint64
Session string Session string
Namespace string `json:",omitempty"`
} }
// KVTxnOps defines a set of operations to be performed inside a single // KVTxnOps defines a set of operations to be performed inside a single

View File

@ -74,6 +74,10 @@ The table below shows this endpoint's support for
- `Session` `(string: "")` - Specifies a session. See the table below for more - `Session` `(string: "")` - Specifies a session. See the table below for more
information. information.
- `Namespace` `(string: "")` - **(Enterprise Only)** Specifies the namespace to
create the KV data If not provided, the namespace will be inherited from the
request's ACL token or will default to the `default` namespace. Added in Consul 1.7.0.
- `Node` operations have the following fields: - `Node` operations have the following fields:
- `Verb` `(string: <required>)` - Specifies the type of operation to perform. - `Verb` `(string: <required>)` - Specifies the type of operation to perform.