mirror of
https://github.com/rancher/steve.git
synced 2025-09-01 07:27:46 +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:
@@ -9,18 +9,18 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rancher/wrangler/v3/pkg/schemas/validation"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/rancher/steve/pkg/accesscontrol"
|
||||
"github.com/rancher/steve/pkg/attributes"
|
||||
"github.com/rancher/steve/pkg/resources/common"
|
||||
"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/sqlpartition/listprocessor"
|
||||
"github.com/rancher/steve/pkg/stores/sqlproxy/tablelistconvert"
|
||||
"github.com/rancher/wrangler/v3/pkg/schemas/validation"
|
||||
|
||||
"go.uber.org/mock/gomock"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rancher/apiserver/pkg/apierror"
|
||||
@@ -29,10 +29,14 @@ import (
|
||||
"github.com/rancher/wrangler/v3/pkg/schemas"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
schema2 "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
krequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/dynamic/fake"
|
||||
"k8s.io/client-go/rest"
|
||||
@@ -621,6 +625,118 @@ func TestListByPartitions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestListByPartitionWithUserAccess(t *testing.T) {
|
||||
type testCase struct {
|
||||
description string
|
||||
accessSetSetter func(accessSet *accesscontrol.AccessSet)
|
||||
orFilters []sqltypes.OrFilter
|
||||
}
|
||||
var tests []testCase
|
||||
tests = append(tests, testCase{
|
||||
description: "client ListByPartitions(), with a specified user for a restricted resource should filter for that user",
|
||||
accessSetSetter: func(accessSet *accesscontrol.AccessSet) {},
|
||||
orFilters: []sqltypes.OrFilter{
|
||||
{
|
||||
Filters: []sqltypes.Filter{
|
||||
{
|
||||
Field: []string{"metadata", "labels", "cattle.io/user-id"},
|
||||
Matches: []string{"flip"},
|
||||
Op: sqltypes.Eq,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
tests = append(tests, testCase{
|
||||
description: "client ListByPartitions(), with an admin user for a restricted resource will return all items regardless of user",
|
||||
accessSetSetter: func(accessSet *accesscontrol.AccessSet) {
|
||||
// admins also get this access-set
|
||||
accessSet.Add("list",
|
||||
schema2.GroupResource{Group: accesscontrol.All, Resource: accesscontrol.All},
|
||||
accesscontrol.Access{Namespace: accesscontrol.All, ResourceName: accesscontrol.All},
|
||||
)
|
||||
},
|
||||
orFilters: []sqltypes.OrFilter{},
|
||||
})
|
||||
t.Parallel()
|
||||
for _, test := range tests {
|
||||
t.Run(test.description, func(t *testing.T) {
|
||||
nsi := NewMockCache(gomock.NewController(t))
|
||||
cg := NewMockClientGetter(gomock.NewController(t))
|
||||
cf := NewMockCacheFactory(gomock.NewController(t))
|
||||
ri := NewMockResourceInterface(gomock.NewController(t))
|
||||
bloi := NewMockByOptionsLister(gomock.NewController(t))
|
||||
tb := NewMockTransformBuilder(gomock.NewController(t))
|
||||
inf := &informer.Informer{
|
||||
ByOptionsLister: bloi,
|
||||
}
|
||||
c := factory.Cache{
|
||||
ByOptionsLister: inf,
|
||||
}
|
||||
s := &Store{
|
||||
ctx: context.Background(),
|
||||
namespaceCache: nsi,
|
||||
clientGetter: cg,
|
||||
cacheFactory: cf,
|
||||
transformBuilder: tb,
|
||||
}
|
||||
var partitions []partition.Partition
|
||||
username := "flip"
|
||||
targetGroup := "ext.cattle.io"
|
||||
targetKind := "Token"
|
||||
accessSet := &accesscontrol.AccessSet{ID: username}
|
||||
accessSet.Add("list",
|
||||
schema2.GroupResource{Group: targetGroup, Resource: "token"},
|
||||
accesscontrol.Access{Namespace: accesscontrol.All, ResourceName: "token"},
|
||||
)
|
||||
test.accessSetSetter(accessSet)
|
||||
apiOpSchemas := &types.APISchemas{}
|
||||
accesscontrol.SetAccessSetAttribute(apiOpSchemas, accessSet)
|
||||
theRequest := &http.Request{
|
||||
URL: &url.URL{},
|
||||
}
|
||||
userInfo := user.DefaultInfo{Name: username, UID: "Id"}
|
||||
requestWithContext := krequest.WithUser(context.Background(), &userInfo)
|
||||
theRequest = theRequest.WithContext(requestWithContext)
|
||||
apiOp := &types.APIRequest{
|
||||
Request: theRequest,
|
||||
Schemas: apiOpSchemas,
|
||||
}
|
||||
theSchema := &types.APISchema{
|
||||
Schema: &schemas.Schema{Attributes: map[string]interface{}{
|
||||
"columns": []common.ColumnDefinition{
|
||||
{
|
||||
Field: "some.field",
|
||||
},
|
||||
},
|
||||
"verbs": []string{"list", "watch"},
|
||||
}},
|
||||
}
|
||||
gvk := schema2.GroupVersionKind{
|
||||
Group: targetGroup,
|
||||
Kind: targetKind,
|
||||
}
|
||||
opts := &sqltypes.ListOptions{
|
||||
Filters: test.orFilters,
|
||||
Pagination: sqltypes.Pagination{
|
||||
Page: 1,
|
||||
},
|
||||
}
|
||||
attributes.SetGVK(theSchema, gvk)
|
||||
cg.EXPECT().TableAdminClient(apiOp, theSchema, "", &WarningBuffer{}).Return(ri, nil)
|
||||
cf.EXPECT().CacheFor(context.Background(), [][]string{{"some", "field"}, {"id"}, {"metadata", "state", "name"}}, gomock.Any(), &tablelistconvert.Client{ResourceInterface: ri}, attributes.GVK(theSchema), attributes.Namespaced(theSchema), true).Return(c, nil)
|
||||
tb.EXPECT().GetTransformFunc(attributes.GVK(theSchema)).Return(func(obj interface{}) (interface{}, error) { return obj, nil })
|
||||
|
||||
listToReturn := &unstructured.UnstructuredList{
|
||||
Items: make([]unstructured.Unstructured, 0, 0),
|
||||
}
|
||||
bloi.EXPECT().ListByOptions(apiOp.Context(), opts, partitions, "").Return(listToReturn, len(listToReturn.Items), "", nil)
|
||||
_, _, _, err := s.ListByPartitions(apiOp, theSchema, partitions)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReset(t *testing.T) {
|
||||
type testCase struct {
|
||||
description string
|
||||
|
Reference in New Issue
Block a user