2020-02-10 17:18:20 +00:00
|
|
|
package proxy
|
|
|
|
|
|
|
|
import (
|
2020-06-05 20:30:33 +00:00
|
|
|
"fmt"
|
2020-02-10 17:18:20 +00:00
|
|
|
"sort"
|
|
|
|
|
2020-06-12 04:50:59 +00:00
|
|
|
"github.com/rancher/apiserver/pkg/types"
|
2020-02-10 17:18:20 +00:00
|
|
|
"github.com/rancher/steve/pkg/accesscontrol"
|
|
|
|
"github.com/rancher/steve/pkg/attributes"
|
2020-06-12 04:50:59 +00:00
|
|
|
"github.com/rancher/steve/pkg/stores/partition"
|
2020-06-22 16:42:18 +00:00
|
|
|
"github.com/rancher/wrangler/pkg/kv"
|
2020-02-10 17:18:20 +00:00
|
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
|
|
)
|
|
|
|
|
2020-06-05 20:30:33 +00:00
|
|
|
var (
|
|
|
|
passthroughPartitions = []partition.Partition{
|
|
|
|
Partition{Passthrough: true},
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
type Partition struct {
|
|
|
|
Namespace string
|
|
|
|
All bool
|
|
|
|
Passthrough bool
|
|
|
|
Names sets.String
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p Partition) Name() string {
|
|
|
|
return p.Namespace
|
|
|
|
}
|
|
|
|
|
|
|
|
type rbacPartitioner struct {
|
|
|
|
proxyStore *Store
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *rbacPartitioner) Lookup(apiOp *types.APIRequest, schema *types.APISchema, verb, id string) (partition.Partition, error) {
|
|
|
|
switch verb {
|
2020-06-12 21:18:13 +00:00
|
|
|
case "create":
|
|
|
|
fallthrough
|
2020-06-05 20:30:33 +00:00
|
|
|
case "get":
|
|
|
|
fallthrough
|
|
|
|
case "update":
|
|
|
|
fallthrough
|
|
|
|
case "delete":
|
|
|
|
return passthroughPartitions[0], nil
|
|
|
|
default:
|
2020-06-12 21:18:13 +00:00
|
|
|
return nil, fmt.Errorf("partition list: invalid verb %s", verb)
|
2020-06-05 20:30:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-22 16:42:18 +00:00
|
|
|
func (p *rbacPartitioner) All(apiOp *types.APIRequest, schema *types.APISchema, verb, id string) ([]partition.Partition, error) {
|
2020-06-05 20:30:33 +00:00
|
|
|
switch verb {
|
|
|
|
case "list":
|
|
|
|
fallthrough
|
|
|
|
case "watch":
|
2020-06-22 16:42:18 +00:00
|
|
|
if id != "" {
|
|
|
|
ns, name := kv.RSplit(id, "/")
|
|
|
|
return []partition.Partition{
|
|
|
|
Partition{
|
|
|
|
Namespace: ns,
|
|
|
|
All: false,
|
|
|
|
Passthrough: false,
|
|
|
|
Names: sets.NewString(name),
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
2020-06-05 20:30:33 +00:00
|
|
|
partitions, passthrough := isPassthrough(apiOp, schema, verb)
|
|
|
|
if passthrough {
|
|
|
|
return passthroughPartitions, nil
|
|
|
|
}
|
|
|
|
sort.Slice(partitions, func(i, j int) bool {
|
|
|
|
return partitions[i].(Partition).Namespace < partitions[j].(Partition).Namespace
|
|
|
|
})
|
|
|
|
return partitions, nil
|
|
|
|
default:
|
2020-06-12 21:18:13 +00:00
|
|
|
return nil, fmt.Errorf("parition all: invalid verb %s", verb)
|
2020-06-05 20:30:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *rbacPartitioner) Store(apiOp *types.APIRequest, partition partition.Partition) (types.Store, error) {
|
|
|
|
return &byNameOrNamespaceStore{
|
|
|
|
Store: p.proxyStore,
|
|
|
|
partition: partition.(Partition),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type byNameOrNamespaceStore struct {
|
2020-02-10 17:18:20 +00:00
|
|
|
*Store
|
2020-06-05 20:30:33 +00:00
|
|
|
partition Partition
|
2020-02-10 17:18:20 +00:00
|
|
|
}
|
|
|
|
|
2020-06-05 20:30:33 +00:00
|
|
|
func (b *byNameOrNamespaceStore) List(apiOp *types.APIRequest, schema *types.APISchema) (types.APIObjectList, error) {
|
|
|
|
if b.partition.Passthrough {
|
|
|
|
return b.Store.List(apiOp, schema)
|
|
|
|
}
|
|
|
|
|
|
|
|
apiOp.Namespace = b.partition.Namespace
|
|
|
|
if b.partition.All {
|
|
|
|
return b.Store.List(apiOp, schema)
|
|
|
|
}
|
|
|
|
return b.Store.ByNames(apiOp, schema, b.partition.Names)
|
2020-02-10 17:18:20 +00:00
|
|
|
}
|
|
|
|
|
2020-06-05 20:30:33 +00:00
|
|
|
func (b *byNameOrNamespaceStore) Watch(apiOp *types.APIRequest, schema *types.APISchema, wr types.WatchRequest) (chan types.APIEvent, error) {
|
|
|
|
if b.partition.Passthrough {
|
|
|
|
return b.Store.Watch(apiOp, schema, wr)
|
|
|
|
}
|
|
|
|
|
|
|
|
apiOp.Namespace = b.partition.Namespace
|
|
|
|
if b.partition.All {
|
|
|
|
return b.Store.Watch(apiOp, schema, wr)
|
|
|
|
}
|
|
|
|
return b.Store.WatchNames(apiOp, schema, wr, b.partition.Names)
|
|
|
|
}
|
|
|
|
|
2022-10-11 19:28:51 +00:00
|
|
|
// isPassthrough determines whether a request can be passed through directly to the underlying store
|
|
|
|
// or if the results need to be partitioned by namespace and name based on the requester's access.
|
2020-06-05 20:30:33 +00:00
|
|
|
func isPassthrough(apiOp *types.APIRequest, schema *types.APISchema, verb string) ([]partition.Partition, bool) {
|
2020-02-10 17:18:20 +00:00
|
|
|
accessListByVerb, _ := attributes.Access(schema).(accesscontrol.AccessListByVerb)
|
|
|
|
if accessListByVerb.All(verb) {
|
|
|
|
return nil, true
|
|
|
|
}
|
|
|
|
|
|
|
|
resources := accessListByVerb.Granted(verb)
|
|
|
|
if apiOp.Namespace != "" {
|
|
|
|
if resources[apiOp.Namespace].All {
|
|
|
|
return nil, true
|
|
|
|
}
|
2022-10-14 20:21:17 +00:00
|
|
|
return []partition.Partition{
|
|
|
|
Partition{
|
|
|
|
Namespace: apiOp.Namespace,
|
|
|
|
Names: resources[apiOp.Namespace].Names,
|
|
|
|
},
|
|
|
|
}, false
|
2020-02-10 17:18:20 +00:00
|
|
|
}
|
|
|
|
|
2020-06-05 20:30:33 +00:00
|
|
|
var result []partition.Partition
|
2020-02-10 17:18:20 +00:00
|
|
|
|
|
|
|
if attributes.Namespaced(schema) {
|
|
|
|
for k, v := range resources {
|
|
|
|
result = append(result, Partition{
|
|
|
|
Namespace: k,
|
|
|
|
All: v.All,
|
|
|
|
Names: v.Names,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for _, v := range resources {
|
|
|
|
result = append(result, Partition{
|
|
|
|
All: v.All,
|
|
|
|
Names: v.Names,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, false
|
|
|
|
}
|