diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index de8092d116..87a1df424c 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -11,7 +11,17 @@ import ( ) // CanIOptions provides utility to check if user has authorization for the given operation -type CanIOptions struct { +type CanIOptions interface { + // RunAccessCheck checks if the caller can perform the operation + // - operation is a combination of namespace, kind, verb + // - can only evaluate a single verb + // - group version resource is determined from the kind using the discovery client REST mapper + // - If disallowed, the reason and evaluationError is available in the logs + // - each can generates a SelfSubjectAccessReview resource and response is evaluated for permissions + RunAccessCheck() (bool, error) +} + +type canIOptions struct { namespace string verb string kind string @@ -19,8 +29,8 @@ type CanIOptions struct { } // NewCanI returns a new instance of operation access controller evaluator -func NewCanI(client dclient.Interface, kind, namespace, verb string) *CanIOptions { - return &CanIOptions{ +func NewCanI(client dclient.Interface, kind, namespace, verb string) CanIOptions { + return &canIOptions{ namespace: namespace, kind: kind, verb: verb, @@ -34,7 +44,7 @@ func NewCanI(client dclient.Interface, kind, namespace, verb string) *CanIOption // - group version resource is determined from the kind using the discovery client REST mapper // - If disallowed, the reason and evaluationError is available in the logs // - each can generates a SelfSubjectAccessReview resource and response is evaluated for permissions -func (o *CanIOptions) RunAccessCheck() (bool, error) { +func (o *canIOptions) RunAccessCheck() (bool, error) { // get GroupVersionResource from RESTMapper // get GVR from kind gvr, err := o.client.Discovery().GetGVRFromKind(o.kind) diff --git a/pkg/auth/auth_test.go b/pkg/auth/auth_test.go index 951bd88fc1..5b8987b1d9 100644 --- a/pkg/auth/auth_test.go +++ b/pkg/auth/auth_test.go @@ -1,48 +1,92 @@ package auth -// import ( -// "testing" -// "time" +import ( + "testing" -// "github.com/golang/glog" -// "github.com/kyverno/kyverno/pkg/config" -// dclient "github.com/kyverno/kyverno/pkg/clients/dclient" -// "github.com/kyverno/kyverno/pkg/signal" -// ) + "github.com/kyverno/kyverno/pkg/clients/dclient" + "github.com/stretchr/testify/assert" +) -// func Test_Auth_pass(t *testing.T) { -// // needs running cluster -// var kubeconfig string -// stopCh := signal.SetupSignalHandler() -// kubeconfig = "/Users/shivd/.kube/config" -// clientConfig, err := config.CreateClientConfig(kubeconfig) -// if err != nil { -// glog.Fatalf("Error building kubeconfig: %v\n", err) -// } +func TestNewCanI(t *testing.T) { + type args struct { + client dclient.Interface + kind string + namespace string + verb string + } + tests := []struct { + name string + args args + }{{ + name: "deployments", + args: args{ + client: dclient.NewEmptyFakeClient(), + kind: "Deployment", + namespace: "default", + verb: "test", + }, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := NewCanI(tt.args.client, tt.args.kind, tt.args.namespace, tt.args.verb) + assert.NotNil(t, got) + }) + } +} -// // DYNAMIC CLIENT -// // - client for all registered resources -// // - invalidate local cache of registered resource every 10 seconds -// client, err := dclient.NewClient(clientConfig, 10*time.Second, stopCh) -// if err != nil { -// glog.Fatalf("Error creating client: %v\n", err) -// } - -// // Can i authenticate - -// kind := "Deployment" -// namespace := "default" -// verb := "test" -// canI := NewCanI(client, kind, namespace, verb) -// ok, err := canI.RunAccessCheck() -// if err != nil { -// t.Error(err) -// } -// if ok { -// t.Log("allowed") -// } else { -// t.Log("notallowed") -// } -// t.FailNow() - -// } +func TestCanIOptions_RunAccessCheck(t *testing.T) { + type fields struct { + namespace string + verb string + kind string + client dclient.Interface + } + tests := []struct { + name string + fields fields + want bool + wantErr bool + }{{ + name: "deployments", + fields: fields{ + client: dclient.NewEmptyFakeClient(), + kind: "Deployment", + namespace: "default", + verb: "test", + }, + want: false, + wantErr: false, + }, { + name: "unknown", + fields: fields{ + client: dclient.NewEmptyFakeClient(), + kind: "Unknown", + namespace: "default", + verb: "test", + }, + want: false, + wantErr: true, + }, { + name: "v2 pods", + fields: fields{ + client: dclient.NewEmptyFakeClient(), + kind: "v2/Pod", + namespace: "default", + verb: "test", + }, + want: false, + wantErr: true, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + o := NewCanI(tt.fields.client, tt.fields.kind, tt.fields.namespace, tt.fields.verb) + got, err := o.RunAccessCheck() + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.want, got) + } + }) + } +} diff --git a/pkg/clients/dclient/fake.go b/pkg/clients/dclient/fake.go index 00d3280dc5..46b67e7f5f 100644 --- a/pkg/clients/dclient/fake.go +++ b/pkg/clients/dclient/fake.go @@ -31,8 +31,9 @@ func NewEmptyFakeClient() Interface { scheme := runtime.NewScheme() return &client{ - client: fake.NewSimpleDynamicClientWithCustomListKinds(scheme, gvrToListKind, objects...), - kclient: kubefake.NewSimpleClientset(objects...), + client: fake.NewSimpleDynamicClientWithCustomListKinds(scheme, gvrToListKind, objects...), + kclient: kubefake.NewSimpleClientset(objects...), + discoveryClient: NewFakeDiscoveryClient(nil), } }