Skip to content

Commit 3b0da75

Browse files
embikxmudrii
andcommitted
Add e2e test case TestAPIExportBindingAuthorizer
On-behalf-of: SAP <marvin.beckers@sap.com> Co-authored-by: Marko Mudrinić <mudrinic.mare@gmail.com> Signed-off-by: Marvin Beckers <marvin@kubermatic.com> Signed-off-by: Marko Mudrinić <mudrinic.mare@gmail.com> Signed-off-by: Marvin Beckers <marvin@kubermatic.com>
1 parent 00bd786 commit 3b0da75

File tree

1 file changed

+262
-0
lines changed

1 file changed

+262
-0
lines changed

test/e2e/virtual/apiexport/authorizer_test.go

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
"github.com/stretchr/testify/require"
2828

29+
corev1 "k8s.io/api/core/v1"
2930
rbacv1 "k8s.io/api/rbac/v1"
3031
"k8s.io/apiextensions-apiserver/pkg/apihelpers"
3132
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -472,13 +473,274 @@ metadata:
472473
}, wait.ForeverTestTimeout, 100*time.Millisecond, "expected service-provider-2-admin to get a forbidden for shadowed cowboy resources")
473474
}
474475

476+
func TestAPIExportBindingAuthorizer(t *testing.T) {
477+
t.Parallel()
478+
framework.Suite(t, "control-plane")
479+
480+
server := kcptesting.SharedKcpServer(t)
481+
482+
ctx, cancel := context.WithCancel(context.Background())
483+
t.Cleanup(cancel)
484+
485+
orgPath, _ := framework.NewOrganizationFixture(t, server) //nolint:staticcheck
486+
487+
serviceProviderPath, _ := kcptesting.NewWorkspaceFixture(t, server, orgPath, kcptesting.WithName("service-provider"))
488+
tenantPath, tenantWorkspace := kcptesting.NewWorkspaceFixture(t, server, orgPath, kcptesting.WithName("tenant"))
489+
490+
cfg := server.BaseConfig(t)
491+
492+
serviceProviderAdmin := server.ClientCAUserConfig(t, rest.CopyConfig(cfg), "service-provider-admin")
493+
tenantUser := server.ClientCAUserConfig(t, rest.CopyConfig(cfg), "tenant-user")
494+
495+
kubeClient, err := kcpkubernetesclientset.NewForConfig(rest.CopyConfig(cfg))
496+
require.NoError(t, err)
497+
kcpClient, err := kcpclientset.NewForConfig(rest.CopyConfig(cfg))
498+
require.NoError(t, err)
499+
500+
framework.AdmitWorkspaceAccess(ctx, t, kubeClient, orgPath, []string{"service-provider-admin", "tenant-user"}, nil, false)
501+
framework.AdmitWorkspaceAccess(ctx, t, kubeClient, serviceProviderPath, []string{"service-provider-admin"}, nil, true)
502+
framework.AdmitWorkspaceAccess(ctx, t, kubeClient, tenantPath, []string{"tenant-user"}, nil, true)
503+
504+
t.Logf("install sherriffs API resource schema, API export, permissions for tenant-user to be able to bind to the export in service provider workspace %q", serviceProviderPath)
505+
require.NoError(t, apply(t, ctx, serviceProviderPath, serviceProviderAdmin,
506+
&apisv1alpha1.APIResourceSchema{
507+
ObjectMeta: metav1.ObjectMeta{Name: "today.sheriffs.wild.wild.west"},
508+
Spec: apisv1alpha1.APIResourceSchemaSpec{
509+
Group: "wild.wild.west",
510+
Names: apiextensionsv1.CustomResourceDefinitionNames{Plural: "sheriffs", Singular: "sheriff", Kind: "Sheriff", ListKind: "SheriffList"},
511+
Scope: "Namespaced",
512+
Versions: []apisv1alpha1.APIResourceVersion{
513+
{Name: "v1alpha1", Served: true, Storage: true, Schema: runtime.RawExtension{Raw: []byte(`{"type":"object"}`)}},
514+
},
515+
},
516+
},
517+
&apisv1alpha1.APIExport{
518+
ObjectMeta: metav1.ObjectMeta{Name: "wild.wild.west"},
519+
Spec: apisv1alpha1.APIExportSpec{
520+
LatestResourceSchemas: []string{"today.sheriffs.wild.wild.west"},
521+
PermissionClaims: []apisv1alpha1.PermissionClaim{
522+
{
523+
GroupResource: apisv1alpha1.GroupResource{
524+
Group: "",
525+
Resource: "configmaps",
526+
},
527+
All: true,
528+
},
529+
},
530+
},
531+
},
532+
533+
&rbacv1.ClusterRole{
534+
ObjectMeta: metav1.ObjectMeta{Name: "tenant-user-bind-apiexport"},
535+
Rules: []rbacv1.PolicyRule{
536+
{APIGroups: []string{"apis.kcp.io"}, ResourceNames: []string{"wild.wild.west"}, Resources: []string{"apiexports"}, Verbs: []string{"bind"}},
537+
},
538+
},
539+
&rbacv1.ClusterRoleBinding{
540+
ObjectMeta: metav1.ObjectMeta{Name: "tenant-user-bind-apiexport"},
541+
Subjects: []rbacv1.Subject{{Kind: "User", Name: "tenant-user"}},
542+
RoleRef: rbacv1.RoleRef{APIGroup: rbacv1.SchemeGroupVersion.Group, Kind: "ClusterRole", Name: "tenant-user-bind-apiexport"},
543+
},
544+
))
545+
546+
t.Logf("Create virtual workspace client for \"today-sherriffs\" APIExport in workspace %q", serviceProviderPath)
547+
serviceProviderAdminApiExportVWCfg := rest.CopyConfig(serviceProviderAdmin)
548+
serviceProviderAdminClient, err := kcpclientset.NewForConfig(serviceProviderAdmin)
549+
require.NoError(t, err)
550+
kcptestinghelpers.Eventually(t, func() (bool, string) {
551+
apiExport, err := serviceProviderAdminClient.Cluster(serviceProviderPath).ApisV1alpha1().APIExports().Get(ctx, "wild.wild.west", metav1.GetOptions{})
552+
require.NoError(t, err)
553+
var found bool
554+
serviceProviderAdminApiExportVWCfg.Host, found, err = framework.VirtualWorkspaceURL(ctx, kcpClient, tenantWorkspace, framework.ExportVirtualWorkspaceURLs(apiExport))
555+
require.NoError(t, err)
556+
//nolint:staticcheck // SA1019 VirtualWorkspaces is deprecated but not removed yet
557+
return found, fmt.Sprintf("waiting for virtual workspace URLs to be available: %v", apiExport.Status.VirtualWorkspaces)
558+
}, wait.ForeverTestTimeout, time.Millisecond*100)
559+
560+
serviceProviderDynamicVWClientForTenantWorkspace, err := kcpdynamic.NewForConfig(serviceProviderAdminApiExportVWCfg)
561+
require.NoError(t, err)
562+
563+
configMap := &corev1.ConfigMap{
564+
TypeMeta: metav1.TypeMeta{
565+
Kind: "ConfigMap",
566+
APIVersion: "v1",
567+
},
568+
ObjectMeta: metav1.ObjectMeta{
569+
Name: "default",
570+
},
571+
Data: map[string]string{
572+
"a": "b",
573+
},
574+
}
575+
u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(configMap)
576+
require.NoError(t, err)
577+
cm := &unstructured.Unstructured{Object: u}
578+
579+
t.Logf("trying to create a ConfigMap in the tenant workspace before creating the APIBinding")
580+
_, err = serviceProviderDynamicVWClientForTenantWorkspace.Cluster(logicalcluster.NewPath(tenantWorkspace.Spec.Cluster)).
581+
Resource(schema.GroupVersionResource{Version: "v1", Resource: "configmaps", Group: ""}).Namespace("default").Create(ctx, cm, metav1.CreateOptions{})
582+
require.True(t, apierrors.IsForbidden(err), "expected to be forbidden from creating ConfigMap")
583+
584+
t.Logf("trying to create a Sheriff in the tenant workspace before creating the APIBinding")
585+
_, err = serviceProviderDynamicVWClientForTenantWorkspace.Cluster(logicalcluster.NewPath(tenantWorkspace.Spec.Cluster)).
586+
Resource(schema.GroupVersionResource{Version: "v1", Resource: "sheriffs", Group: ""}).Namespace("default").Create(ctx, &unstructured.Unstructured{
587+
Object: map[string]any{
588+
"apiVersion": "wild.wild.west/v1alpha1",
589+
"kind": "Sheriff",
590+
"metadata": map[string]any{
591+
"name": "default",
592+
},
593+
},
594+
}, metav1.CreateOptions{})
595+
require.True(t, apierrors.IsForbidden(err), "expected to be forbidden from creating Sheriff")
596+
597+
t.Logf("bind sherriffs with ConfigMaps PermissionClaim rejected in the tenant workspace %q", tenantPath)
598+
kcptestinghelpers.Eventually(t, func() (success bool, reason string) {
599+
err := apply(t, ctx, tenantPath, tenantUser,
600+
&apisv1alpha1.APIBinding{
601+
ObjectMeta: metav1.ObjectMeta{
602+
Name: "wild.wild.west",
603+
},
604+
Spec: apisv1alpha1.APIBindingSpec{
605+
PermissionClaims: []apisv1alpha1.AcceptablePermissionClaim{
606+
{
607+
PermissionClaim: apisv1alpha1.PermissionClaim{
608+
GroupResource: apisv1alpha1.GroupResource{Resource: "configmaps"},
609+
All: true,
610+
},
611+
State: apisv1alpha1.ClaimRejected,
612+
},
613+
},
614+
Reference: apisv1alpha1.BindingReference{
615+
Export: &apisv1alpha1.ExportBindingReference{
616+
Path: serviceProviderPath.String(),
617+
Name: "wild.wild.west",
618+
},
619+
},
620+
},
621+
},
622+
)
623+
if err != nil {
624+
return false, err.Error()
625+
}
626+
return true, ""
627+
}, wait.ForeverTestTimeout, time.Millisecond*100)
628+
629+
t.Logf("trying to create a ConfigMap in the tenant workspace after creating the APIBinding with rejected PermissionClaim")
630+
_, err = serviceProviderDynamicVWClientForTenantWorkspace.Cluster(logicalcluster.NewPath(tenantWorkspace.Spec.Cluster)).
631+
Resource(schema.GroupVersionResource{Version: "v1", Resource: "configmaps", Group: ""}).Namespace("default").Create(ctx, cm, metav1.CreateOptions{})
632+
require.True(t, apierrors.IsForbidden(err), "expected to be forbidden from creating ConfigMap")
633+
634+
t.Logf("update sherriffs APIBinding to accept the ConfigMaps PermissionClaim")
635+
kcptestinghelpers.Eventually(t, func() (success bool, reason string) {
636+
err := apply(t, ctx, tenantPath, tenantUser,
637+
&apisv1alpha1.APIBinding{
638+
ObjectMeta: metav1.ObjectMeta{
639+
Name: "wild.wild.west",
640+
},
641+
Spec: apisv1alpha1.APIBindingSpec{
642+
PermissionClaims: []apisv1alpha1.AcceptablePermissionClaim{
643+
{
644+
PermissionClaim: apisv1alpha1.PermissionClaim{
645+
GroupResource: apisv1alpha1.GroupResource{Resource: "configmaps"},
646+
All: true,
647+
},
648+
State: apisv1alpha1.ClaimAccepted,
649+
},
650+
},
651+
Reference: apisv1alpha1.BindingReference{
652+
Export: &apisv1alpha1.ExportBindingReference{
653+
Path: serviceProviderPath.String(),
654+
Name: "wild.wild.west",
655+
},
656+
},
657+
},
658+
},
659+
)
660+
if err != nil {
661+
return false, err.Error()
662+
}
663+
return true, ""
664+
}, wait.ForeverTestTimeout, time.Millisecond*100)
665+
666+
t.Logf("trying to create a ConfigMap in the tenant workspace after updating the APIBinding with accepted PermissionClaim")
667+
kcptestinghelpers.Eventually(t, func() (bool, string) {
668+
_, err = serviceProviderDynamicVWClientForTenantWorkspace.Cluster(logicalcluster.NewPath(tenantWorkspace.Spec.Cluster)).
669+
Resource(schema.GroupVersionResource{Version: "v1", Resource: "configmaps", Group: ""}).Namespace("default").Create(ctx, cm, metav1.CreateOptions{})
670+
if err != nil {
671+
return false, err.Error()
672+
}
673+
674+
return true, ""
675+
}, wait.ForeverTestTimeout, 100*time.Millisecond, "error creating ConfigMap")
676+
677+
t.Logf("trying to create a Sheriff in the tenant workspace")
678+
kcptestinghelpers.Eventually(t, func() (bool, string) {
679+
_, err = serviceProviderDynamicVWClientForTenantWorkspace.Cluster(logicalcluster.NewPath(tenantWorkspace.Spec.Cluster)).
680+
Resource(schema.GroupVersionResource{Version: "v1alpha1", Resource: "sheriffs", Group: "wild.wild.west"}).Namespace("default").Create(ctx, &unstructured.Unstructured{
681+
Object: map[string]any{
682+
"apiVersion": "wild.wild.west/v1alpha1",
683+
"kind": "Sheriff",
684+
"metadata": map[string]any{
685+
"name": "default",
686+
},
687+
},
688+
}, metav1.CreateOptions{})
689+
if err != nil {
690+
return false, err.Error()
691+
}
692+
return true, ""
693+
}, wait.ForeverTestTimeout, 100*time.Millisecond, "error creating Sheriff")
694+
695+
t.Logf("update sherriffs APIBinding to reject the ConfigMaps PermissionClaim again")
696+
kcptestinghelpers.Eventually(t, func() (success bool, reason string) {
697+
err := apply(t, ctx, tenantPath, tenantUser,
698+
&apisv1alpha1.APIBinding{
699+
ObjectMeta: metav1.ObjectMeta{
700+
Name: "wild.wild.west",
701+
},
702+
Spec: apisv1alpha1.APIBindingSpec{
703+
PermissionClaims: []apisv1alpha1.AcceptablePermissionClaim{
704+
{
705+
PermissionClaim: apisv1alpha1.PermissionClaim{
706+
GroupResource: apisv1alpha1.GroupResource{Resource: "configmaps"},
707+
All: true,
708+
},
709+
State: apisv1alpha1.ClaimRejected,
710+
},
711+
},
712+
Reference: apisv1alpha1.BindingReference{
713+
Export: &apisv1alpha1.ExportBindingReference{
714+
Path: serviceProviderPath.String(),
715+
Name: "wild.wild.west",
716+
},
717+
},
718+
},
719+
},
720+
)
721+
if err != nil {
722+
return false, err.Error()
723+
}
724+
return true, ""
725+
}, wait.ForeverTestTimeout, time.Millisecond*100)
726+
727+
// I know, I know, but we need to wait for the APIBinding update to reach the informer in the authorizer ...
728+
time.Sleep(5 * time.Second)
729+
730+
t.Logf("trying to delete the created ConfigMap in the tenant workspace after updating the APIBinding with rejected PermissionClaim")
731+
err = serviceProviderDynamicVWClientForTenantWorkspace.Cluster(logicalcluster.NewPath(tenantWorkspace.Spec.Cluster)).
732+
Resource(schema.GroupVersionResource{Version: "v1", Resource: "configmaps", Group: ""}).Namespace("default").Delete(ctx, "default", metav1.DeleteOptions{})
733+
require.True(t, apierrors.IsForbidden(err), "expected to be forbidden from deleting ConfigMap")
734+
}
735+
475736
var scheme *runtime.Scheme
476737

477738
func init() {
478739
scheme = runtime.NewScheme()
479740
_ = apisv1alpha1.AddToScheme(scheme)
480741
_ = rbacv1.AddToScheme(scheme)
481742
_ = apiextensionsv1.AddToScheme(scheme)
743+
_ = corev1.AddToScheme(scheme)
482744
}
483745

484746
func apply(t *testing.T, ctx context.Context, workspace logicalcluster.Path, cfg *rest.Config, manifests ...any) error {

0 commit comments

Comments
 (0)