mirror of
https://github.com/rancher/steve.git
synced 2025-09-08 10:49:25 +00:00
Partial extension API server store + control over printed columns (#432)
* Checkpoint * Add support for custom columns * Remove old Store and Delegate abstraction * Fix nits and rewording * Remove unused mock file * Update documentation for extension api server * Remove the need for scheme for ConvertListOptions * Rename store to utils * fixup! Remove the need for scheme for ConvertListOptions * Move watch helper to tests * Add convertError at a few places * Ignore misspell on creater * Fix comments and remove unused params * Add convertError to missing error returns * Fix watcher implementation * Document request.UserFrom and request.NamespaceFrom
This commit is contained in:
@@ -23,32 +23,40 @@ import (
|
||||
"go.uber.org/mock/gomock"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
yamlutil "k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/server/options"
|
||||
)
|
||||
|
||||
type authzTestStore struct {
|
||||
*testStore
|
||||
*testStore[*TestType, *TestTypeList]
|
||||
authorizer authorizer.Authorizer
|
||||
}
|
||||
|
||||
func (t *authzTestStore) Get(ctx Context, name string, opts *metav1.GetOptions) (*TestType, error) {
|
||||
if name == "not-found" {
|
||||
return nil, apierrors.NewNotFound(ctx.GroupVersionResource.GroupResource(), name)
|
||||
// Get implements [rest.Getter]
|
||||
func (t *authzTestStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
return t.get(ctx, name, options)
|
||||
}
|
||||
|
||||
// List implements [rest.Lister]
|
||||
func (t *authzTestStore) List(ctx context.Context, _ *metainternalversion.ListOptions) (runtime.Object, error) {
|
||||
userInfo, ok := request.UserFrom(ctx)
|
||||
if !ok {
|
||||
return nil, convertError(fmt.Errorf("missing user info"))
|
||||
}
|
||||
return t.testStore.Get(ctx, name, opts)
|
||||
}
|
||||
|
||||
func (t *authzTestStore) List(ctx Context, opts *metav1.ListOptions) (*TestTypeList, error) {
|
||||
if ctx.User.GetName() == "read-only-error" {
|
||||
decision, _, err := ctx.Authorizer.Authorize(ctx, authorizer.AttributesRecord{
|
||||
User: ctx.User,
|
||||
if userInfo.GetName() == "read-only-error" {
|
||||
decision, _, err := t.authorizer.Authorize(ctx, authorizer.AttributesRecord{
|
||||
User: userInfo,
|
||||
Verb: "customverb",
|
||||
Resource: "testtypes",
|
||||
ResourceRequest: true,
|
||||
@@ -58,7 +66,7 @@ func (t *authzTestStore) List(ctx Context, opts *metav1.ListOptions) (*TestTypeL
|
||||
if err == nil {
|
||||
err = fmt.Errorf("not allowed")
|
||||
}
|
||||
forbidden := apierrors.NewForbidden(ctx.GroupVersionResource.GroupResource(), "Forbidden", err)
|
||||
forbidden := apierrors.NewForbidden(t.gvr.GroupResource(), "Forbidden", err)
|
||||
forbidden.ErrStatus.Kind = "Status"
|
||||
forbidden.ErrStatus.APIVersion = "v1"
|
||||
return nil, forbidden
|
||||
@@ -67,6 +75,54 @@ func (t *authzTestStore) List(ctx Context, opts *metav1.ListOptions) (*TestTypeL
|
||||
return &testTypeListFixture, nil
|
||||
}
|
||||
|
||||
func (t *authzTestStore) get(_ context.Context, name string, _ *metav1.GetOptions) (*TestType, error) {
|
||||
if name == "not-found" {
|
||||
return nil, apierrors.NewNotFound(t.gvr.GroupResource(), name)
|
||||
}
|
||||
return &testTypeFixture, nil
|
||||
}
|
||||
|
||||
func (t *authzTestStore) create(_ context.Context, _ *TestType, _ *metav1.CreateOptions) (*TestType, error) {
|
||||
return &testTypeFixture, nil
|
||||
}
|
||||
|
||||
func (t *authzTestStore) update(_ context.Context, _ *TestType, _ *metav1.UpdateOptions) (*TestType, error) {
|
||||
return &testTypeFixture, nil
|
||||
}
|
||||
|
||||
// Create implements [rest.Creater]
|
||||
func (t *authzTestStore) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
|
||||
if createValidation != nil {
|
||||
err := createValidation(ctx, obj)
|
||||
if err != nil {
|
||||
return obj, err
|
||||
}
|
||||
}
|
||||
|
||||
objT, ok := obj.(*TestType)
|
||||
if !ok {
|
||||
var zeroT *TestType
|
||||
return nil, convertError(fmt.Errorf("expected %T but got %T", zeroT, obj))
|
||||
}
|
||||
|
||||
return t.create(ctx, objT, options)
|
||||
}
|
||||
|
||||
// Update implements [rest.Updater]
|
||||
func (t *authzTestStore) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
|
||||
return CreateOrUpdate(ctx, name, objInfo, createValidation, updateValidation, forceAllowCreate, options, t.get, t.create, t.update)
|
||||
}
|
||||
|
||||
// Watch implements [rest.Watcher]
|
||||
func (t *authzTestStore) Watch(_ context.Context, _ *metainternalversion.ListOptions) (watch.Interface, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Delete implements [rest.GracefulDeleter]
|
||||
func (t *authzTestStore) Delete(_ context.Context, _ string, _ rest.ValidateObjectFunc, _ *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (s *ExtensionAPIServerSuite) TestAuthorization() {
|
||||
t := s.T()
|
||||
|
||||
@@ -89,10 +145,7 @@ func (s *ExtensionAPIServerSuite) TestAuthorization() {
|
||||
ln, _, err := options.CreateListener("", ":0", net.ListenConfig{})
|
||||
require.NoError(t, err)
|
||||
|
||||
store := &authzTestStore{
|
||||
testStore: &testStore{},
|
||||
}
|
||||
extensionAPIServer, cleanup, err := setupExtensionAPIServer(t, scheme, &TestType{}, &TestTypeList{}, store, func(opts *ExtensionAPIServerOptions) {
|
||||
extensionAPIServer, cleanup, err := setupExtensionAPIServerNoStore(t, scheme, func(opts *ExtensionAPIServerOptions) {
|
||||
opts.Listener = ln
|
||||
opts.Authorizer = authz
|
||||
opts.Authenticator = authenticator.RequestFunc(func(req *http.Request) (*authenticator.Response, bool, error) {
|
||||
@@ -104,7 +157,17 @@ func (s *ExtensionAPIServerSuite) TestAuthorization() {
|
||||
User: user,
|
||||
}, true, nil
|
||||
})
|
||||
}, nil)
|
||||
}, func(s *ExtensionAPIServer) error {
|
||||
store := &authzTestStore{
|
||||
testStore: newDefaultTestStore(),
|
||||
authorizer: s.GetAuthorizer(),
|
||||
}
|
||||
err := s.Install("testtypes", testTypeGV.WithKind("TestType"), store)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer cleanup()
|
||||
|
||||
|
Reference in New Issue
Block a user