mirror of
https://github.com/rancher/steve.git
synced 2025-09-15 06:49:27 +00:00
Add vai access-control for ext Tokens and Kubeconfigs (#651)
* Add vai access-control for tokens. * Check for both Token and Kubeconfig resources * Add a unit test for verifying the generated filters for restricted resources. * Remove a TODO comment as Tom points out we no longer need it. * Return error if we can't get userinfo from apiOp.Request.Context * Stop using camelCase for the user ID label. * Add a test for the admin user. * And fold the two user-access tests into a single parameterized test. * Address reviewer comments. * post-rebase merge fixes * WIP - add a comment about determining admin users. --------- Co-authored-by: Peter Matseykanets <peter.matseykanets@suse.com>
This commit is contained in:
@@ -13,34 +13,21 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rancher/steve/pkg/stores/queryhelper"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
apitypes "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"github.com/rancher/apiserver/pkg/apierror"
|
||||
"github.com/rancher/apiserver/pkg/types"
|
||||
"github.com/rancher/steve/pkg/accesscontrol"
|
||||
"github.com/rancher/steve/pkg/sqlcache/informer"
|
||||
"github.com/rancher/steve/pkg/sqlcache/informer/factory"
|
||||
"github.com/rancher/steve/pkg/sqlcache/partition"
|
||||
"github.com/rancher/steve/pkg/sqlcache/sqltypes"
|
||||
"github.com/rancher/steve/pkg/stores/queryhelper"
|
||||
"github.com/rancher/wrangler/v3/pkg/data"
|
||||
"github.com/rancher/wrangler/v3/pkg/kv"
|
||||
"github.com/rancher/wrangler/v3/pkg/schemas"
|
||||
"github.com/rancher/wrangler/v3/pkg/schemas/validation"
|
||||
"github.com/rancher/wrangler/v3/pkg/summary"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/rancher/steve/pkg/attributes"
|
||||
controllerschema "github.com/rancher/steve/pkg/controllers/schema"
|
||||
@@ -50,6 +37,21 @@ import (
|
||||
metricsStore "github.com/rancher/steve/pkg/stores/metrics"
|
||||
"github.com/rancher/steve/pkg/stores/sqlpartition/listprocessor"
|
||||
"github.com/rancher/steve/pkg/stores/sqlproxy/tablelistconvert"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
apitypes "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -738,28 +740,50 @@ func (s *Store) Delete(apiOp *types.APIRequest, schema *types.APISchema, id stri
|
||||
// - the total number of resources (returned list might be a subset depending on pagination options in apiOp)
|
||||
// - a continue token, if there are more pages after the returned one
|
||||
// - an error instead of all of the above if anything went wrong
|
||||
func (s *Store) ListByPartitions(apiOp *types.APIRequest, schema *types.APISchema, partitions []partition.Partition) (*unstructured.UnstructuredList, int, string, error) {
|
||||
func (s *Store) ListByPartitions(apiOp *types.APIRequest, apiSchema *types.APISchema, partitions []partition.Partition) (*unstructured.UnstructuredList, int, string, error) {
|
||||
opts, err := listprocessor.ParseQuery(apiOp, s.namespaceCache)
|
||||
if err != nil {
|
||||
return nil, 0, "", err
|
||||
}
|
||||
// warnings from inside the informer are discarded
|
||||
buffer := WarningBuffer{}
|
||||
client, err := s.clientGetter.TableAdminClient(apiOp, schema, "", &buffer)
|
||||
client, err := s.clientGetter.TableAdminClient(apiOp, apiSchema, "", &buffer)
|
||||
if err != nil {
|
||||
return nil, 0, "", err
|
||||
}
|
||||
gvk := attributes.GVK(schema)
|
||||
fields := getFieldsFromSchema(schema)
|
||||
gvk := attributes.GVK(apiSchema)
|
||||
fields := getFieldsFromSchema(apiSchema)
|
||||
fields = append(fields, getFieldForGVK(gvk)...)
|
||||
transformFunc := s.transformBuilder.GetTransformFunc(gvk)
|
||||
tableClient := &tablelistconvert.Client{ResourceInterface: client}
|
||||
attrs := attributes.GVK(schema)
|
||||
ns := attributes.Namespaced(schema)
|
||||
inf, err := s.cacheFactory.CacheFor(s.ctx, fields, transformFunc, tableClient, attrs, ns, controllerschema.IsListWatchable(schema))
|
||||
attrs := attributes.GVK(apiSchema)
|
||||
ns := attributes.Namespaced(apiSchema)
|
||||
inf, err := s.cacheFactory.CacheFor(s.ctx, fields, transformFunc, tableClient, attrs, ns, controllerschema.IsListWatchable(apiSchema))
|
||||
if err != nil {
|
||||
return nil, 0, "", err
|
||||
}
|
||||
if gvk.Group == "ext.cattle.io" && (gvk.Kind == "Token" || gvk.Kind == "Kubeconfig") {
|
||||
accessSet := accesscontrol.AccessSetFromAPIRequest(apiOp)
|
||||
// See https://github.com/rancher/rancher/blob/7266e5e624f0d610c76ab0af33e30f5b72e11f61/pkg/ext/stores/tokens/tokens.go#L1186C2-L1195C3
|
||||
// for similar code on how we determine if a user is admin
|
||||
if accessSet == nil || !accessSet.Grants("list", schema.GroupResource{
|
||||
Resource: "*",
|
||||
}, "", "") {
|
||||
user, ok := request.UserFrom(apiOp.Request.Context())
|
||||
if !ok {
|
||||
return nil, 0, "", apierror.NewAPIError(validation.MissingRequired, "failed to get user info from the request.Context object")
|
||||
}
|
||||
opts.Filters = append(opts.Filters, sqltypes.OrFilter{
|
||||
Filters: []sqltypes.Filter{
|
||||
{
|
||||
Field: []string{"metadata", "labels", "cattle.io/user-id"},
|
||||
Matches: []string{user.GetName()},
|
||||
Op: sqltypes.Eq,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
list, total, continueToken, err := inf.ListByOptions(apiOp.Context(), &opts, partitions, apiOp.Namespace)
|
||||
if err != nil {
|
||||
|
Reference in New Issue
Block a user