Files
open-cluster-management/pkg/work/spoke/auth/basic/auth_test.go
Jian Zhu 7332a585c0 🌱 add a verify rule for golang files import order (#177)
* 🌱 add a verify rule for golang files import order

This PR uses the [gci tool](https://github.com/daixiang0/gci) to make all go files' import section with a specific order, it will organize import with group with order:
1. standard library modules
2. 3rd party modules
3. modules in OCM org, like the `open-cluster-management.io/api`
4. current project `open-cluster-management.io/ocm` modules

developers can use the `make fmt-imports` to format the import automatically and the `make verify-fmt-imports` to check for any violation.

Signed-off-by: zhujian <jiazhu@redhat.com>

* 🌱 format the go files import

Signed-off-by: zhujian <jiazhu@redhat.com>

---------

Signed-off-by: zhujian <jiazhu@redhat.com>
2023-06-12 10:23:04 -04:00

211 lines
6.2 KiB
Go

package basic
import (
"context"
"fmt"
"testing"
v1 "k8s.io/api/authorization/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
fakedynamic "k8s.io/client-go/dynamic/fake"
fakekube "k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/rest"
clienttesting "k8s.io/client-go/testing"
workapiv1 "open-cluster-management.io/api/work/v1"
"open-cluster-management.io/ocm/pkg/work/spoke/spoketesting"
)
func TestValidate(t *testing.T) {
tests := map[string]struct {
executor *workapiv1.ManifestWorkExecutor
namespace string
name string
expect error
}{
"executor nil": {
executor: nil,
expect: nil,
},
"unsupported type": {
executor: &workapiv1.ManifestWorkExecutor{
Subject: workapiv1.ManifestWorkExecutorSubject{
Type: "test",
},
},
expect: fmt.Errorf("only support %s type for the executor", workapiv1.ExecutorSubjectTypeServiceAccount),
},
"sa nil": {
executor: &workapiv1.ManifestWorkExecutor{
Subject: workapiv1.ManifestWorkExecutorSubject{
Type: workapiv1.ExecutorSubjectTypeServiceAccount,
},
},
expect: fmt.Errorf("the executor service account is nil"),
},
"forbidden": {
executor: &workapiv1.ManifestWorkExecutor{
Subject: workapiv1.ManifestWorkExecutorSubject{
Type: workapiv1.ExecutorSubjectTypeServiceAccount,
ServiceAccount: &workapiv1.ManifestWorkSubjectServiceAccount{
Namespace: "test-ns",
Name: "test-name",
},
},
},
namespace: "test-deny",
name: "test",
expect: fmt.Errorf("not allowed to apply the resource secrets, test-deny test, will try again in 1m0s"),
},
"allow": {
executor: &workapiv1.ManifestWorkExecutor{
Subject: workapiv1.ManifestWorkExecutorSubject{
Type: workapiv1.ExecutorSubjectTypeServiceAccount,
ServiceAccount: &workapiv1.ManifestWorkSubjectServiceAccount{
Namespace: "test-ns",
Name: "test-name",
},
},
},
namespace: "test-allow",
name: "test",
expect: nil,
},
}
gvr := schema.GroupVersionResource{Version: "v1", Resource: "secrets"}
kubeClient := fakekube.NewSimpleClientset()
kubeClient.PrependReactor("create", "subjectaccessreviews",
func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
obj := action.(clienttesting.CreateActionImpl).Object.(*v1.SubjectAccessReview)
if obj.Spec.ResourceAttributes.Namespace == "test-allow" {
return true, &v1.SubjectAccessReview{
Status: v1.SubjectAccessReviewStatus{
Allowed: true,
},
}, nil
}
if obj.Spec.ResourceAttributes.Namespace == "test-deny" {
return true, &v1.SubjectAccessReview{
Status: v1.SubjectAccessReviewStatus{
Denied: true,
},
}, nil
}
return false, nil, nil
},
)
validator := NewSARValidator(nil, kubeClient)
for testName, test := range tests {
t.Run(testName, func(t *testing.T) {
err := validator.Validate(context.TODO(), test.executor, gvr, test.namespace, test.name, true, nil)
if test.expect == nil {
if err != nil {
t.Errorf("expect nil but got %s", err)
}
} else if err == nil || err.Error() != test.expect.Error() {
t.Errorf("expect %s but got %s", test.expect, err)
}
})
}
}
func TestValidateEscalation(t *testing.T) {
tests := map[string]struct {
executor *workapiv1.ManifestWorkExecutor
namespace string
name string
obj *unstructured.Unstructured
expect error
}{
"forbidden": {
executor: &workapiv1.ManifestWorkExecutor{
Subject: workapiv1.ManifestWorkExecutorSubject{
Type: workapiv1.ExecutorSubjectTypeServiceAccount,
ServiceAccount: &workapiv1.ManifestWorkSubjectServiceAccount{
Namespace: "test-ns",
Name: "test-name",
},
},
},
namespace: "test-deny",
name: "test",
obj: spoketesting.NewUnstructured("v1", "ClusterRole", "", "test"),
expect: fmt.Errorf("not allowed to apply the resource rbac.authorization.k8s.io roles, test-deny test, error: permission escalation, will try again in 1m0s"),
},
"allow": {
executor: &workapiv1.ManifestWorkExecutor{
Subject: workapiv1.ManifestWorkExecutorSubject{
Type: workapiv1.ExecutorSubjectTypeServiceAccount,
ServiceAccount: &workapiv1.ManifestWorkSubjectServiceAccount{
Namespace: "test-ns",
Name: "test-name",
},
},
},
namespace: "test-allow",
name: "test",
obj: spoketesting.NewUnstructured("v1", "Role", "ns1", "test"),
expect: nil,
},
}
gvr := schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "roles"}
kubeClient := fakekube.NewSimpleClientset()
kubeClient.PrependReactor("create", "subjectaccessreviews",
func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &v1.SubjectAccessReview{
Status: v1.SubjectAccessReviewStatus{
Allowed: true,
},
}, nil
},
)
scheme := runtime.NewScheme()
dynamicClient := fakedynamic.NewSimpleDynamicClient(scheme)
dynamicClient.PrependReactor("create", "roles",
func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
obj := action.(clienttesting.CreateActionImpl).Object.(*unstructured.Unstructured)
if obj.GetKind() == "Role" {
return true, obj, nil
}
if obj.GetKind() == "ClusterRole" {
return true, obj, apierrors.NewForbidden(
schema.GroupResource{Group: "rbac.authorization.k8s.io", Resource: "clusterroles"},
obj.GetName(),
fmt.Errorf("escalation"))
}
return false, nil, nil
})
validator := &SarValidator{
kubeClient: kubeClient,
newImpersonateClientFunc: func(config *rest.Config, username string) (dynamic.Interface, error) {
return dynamicClient, nil
},
}
for testName, test := range tests {
t.Run(testName, func(t *testing.T) {
err := validator.Validate(context.TODO(), test.executor, gvr, test.namespace, test.name, true, test.obj)
if test.expect == nil {
if err != nil {
t.Errorf("expect nil but got %s", err)
}
} else if err == nil || err.Error() != test.expect.Error() {
t.Errorf("expect %s but got %s", test.expect, err)
}
})
}
}