Skip to content

Commit 73e6080

Browse files
author
Per Goncalves da Silva
committed
Add unit tests
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent 4ba5499 commit 73e6080

File tree

3 files changed

+157
-8
lines changed

3 files changed

+157
-8
lines changed

internal/operator-controller/action/restconfig.go

+22-8
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,36 @@ import (
1111

1212
ocv1 "github.com/operator-framework/operator-controller/api/v1"
1313
"github.com/operator-framework/operator-controller/internal/operator-controller/authentication"
14+
"github.com/operator-framework/operator-controller/internal/operator-controller/features"
1415
)
1516

16-
func ClusterExtensionUserRestConfigMapper(tokenGetter *authentication.TokenGetter) func(ctx context.Context, o client.Object, c *rest.Config) (*rest.Config, error) {
17-
saRestConfigMapper := serviceAccountRestConfigMapper(tokenGetter)
18-
synthRestConfigMapper := sythenticUserRestConfigMapper()
17+
const syntheticServiceAccountName = "olmv1:synthetic"
18+
19+
type clusterExtensionRestConfigMapper struct {
20+
saRestConfigMapper func(ctx context.Context, o client.Object, c *rest.Config) (*rest.Config, error)
21+
synthUserRestConfigMapper func(ctx context.Context, o client.Object, c *rest.Config) (*rest.Config, error)
22+
}
1923

24+
func (m *clusterExtensionRestConfigMapper) mapper() func(ctx context.Context, o client.Object, c *rest.Config) (*rest.Config, error) {
25+
synthAuthFeatureEnabled := features.OperatorControllerFeatureGate.Enabled(features.SyntheticPermissions)
2026
return func(ctx context.Context, o client.Object, c *rest.Config) (*rest.Config, error) {
2127
cExt := o.(*ocv1.ClusterExtension)
22-
if cExt.Spec.ServiceAccount != nil { //nolint:staticcheck
23-
return saRestConfigMapper(ctx, o, c)
28+
if synthAuthFeatureEnabled && cExt.Spec.ServiceAccount.Name == syntheticServiceAccountName {
29+
return m.synthUserRestConfigMapper(ctx, o, c)
2430
}
25-
return synthRestConfigMapper(ctx, o, c)
31+
return m.saRestConfigMapper(ctx, o, c)
32+
}
33+
}
34+
35+
func ClusterExtensionUserRestConfigMapper(tokenGetter *authentication.TokenGetter) func(ctx context.Context, o client.Object, c *rest.Config) (*rest.Config, error) {
36+
m := &clusterExtensionRestConfigMapper{
37+
saRestConfigMapper: serviceAccountRestConfigMapper(tokenGetter),
38+
synthUserRestConfigMapper: syntheticUserRestConfigMapper(),
2639
}
40+
return m.mapper()
2741
}
2842

29-
func sythenticUserRestConfigMapper() func(ctx context.Context, o client.Object, c *rest.Config) (*rest.Config, error) {
43+
func syntheticUserRestConfigMapper() func(ctx context.Context, o client.Object, c *rest.Config) (*rest.Config, error) {
3044
return func(ctx context.Context, o client.Object, c *rest.Config) (*rest.Config, error) {
3145
cExt := o.(*ocv1.ClusterExtension)
3246
cc := rest.CopyConfig(c)
@@ -41,7 +55,7 @@ func serviceAccountRestConfigMapper(tokenGetter *authentication.TokenGetter) fun
4155
return func(ctx context.Context, o client.Object, c *rest.Config) (*rest.Config, error) {
4256
cExt := o.(*ocv1.ClusterExtension)
4357
saKey := types.NamespacedName{
44-
Name: cExt.Spec.ServiceAccount.Name, //nolint:staticcheck
58+
Name: cExt.Spec.ServiceAccount.Name,
4559
Namespace: cExt.Spec.Namespace,
4660
}
4761
saConfig := rest.AnonymousClientConfig(c)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package action
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
"k8s.io/client-go/rest"
9+
featuregatetesting "k8s.io/component-base/featuregate/testing"
10+
"sigs.k8s.io/controller-runtime/pkg/client"
11+
12+
ocv1 "github.com/operator-framework/operator-controller/api/v1"
13+
"github.com/operator-framework/operator-controller/internal/operator-controller/features"
14+
)
15+
16+
const (
17+
saAccountWrapper = "service account wrapper"
18+
synthUserWrapper = "synthetic user wrapper"
19+
)
20+
21+
func fakeRestConfigWrapper() clusterExtensionRestConfigMapper {
22+
// The rest config's host field is artificially used to differentiate between the wrappers
23+
return clusterExtensionRestConfigMapper{
24+
saRestConfigMapper: func(ctx context.Context, o client.Object, c *rest.Config) (*rest.Config, error) {
25+
return &rest.Config{
26+
Host: saAccountWrapper,
27+
}, nil
28+
},
29+
synthUserRestConfigMapper: func(ctx context.Context, o client.Object, c *rest.Config) (*rest.Config, error) {
30+
return &rest.Config{
31+
Host: synthUserWrapper,
32+
}, nil
33+
},
34+
}
35+
}
36+
37+
func TestMapper_SyntheticPermissionsEnabled(t *testing.T) {
38+
featuregatetesting.SetFeatureGateDuringTest(t, features.OperatorControllerFeatureGate, features.SyntheticPermissions, true)
39+
40+
for _, tc := range []struct {
41+
description string
42+
serviceAccountName string
43+
expectedMapper string
44+
fgEnabled bool
45+
}{
46+
{
47+
description: "user service account wrapper if extension service account is _not_ called olmv1:synthetic",
48+
serviceAccountName: "_not_:olmv1:synthetic",
49+
expectedMapper: saAccountWrapper,
50+
fgEnabled: true,
51+
}, {
52+
description: "user synthetic user wrapper is extension service account is called olmv1:synthetic",
53+
serviceAccountName: "olmv1:synthetic",
54+
expectedMapper: synthUserWrapper,
55+
fgEnabled: true,
56+
},
57+
} {
58+
t.Run(tc.description, func(t *testing.T) {
59+
m := fakeRestConfigWrapper()
60+
mapper := m.mapper()
61+
ext := &ocv1.ClusterExtension{
62+
Spec: ocv1.ClusterExtensionSpec{
63+
ServiceAccount: ocv1.ServiceAccountReference{
64+
Name: tc.serviceAccountName,
65+
},
66+
},
67+
}
68+
cfg, err := mapper(context.Background(), ext, &rest.Config{})
69+
require.NoError(t, err)
70+
71+
// The rest config's host field is artificially used to differentiate between the wrappers
72+
require.Equal(t, tc.expectedMapper, cfg.Host)
73+
})
74+
}
75+
}
76+
77+
func TestMapper_SyntheticPermissionsDisabled(t *testing.T) {
78+
m := fakeRestConfigWrapper()
79+
mapper := m.mapper()
80+
ext := &ocv1.ClusterExtension{
81+
Spec: ocv1.ClusterExtensionSpec{
82+
ServiceAccount: ocv1.ServiceAccountReference{
83+
Name: "olmv1:synthetic",
84+
},
85+
},
86+
}
87+
cfg, err := mapper(context.Background(), ext, &rest.Config{})
88+
require.NoError(t, err)
89+
90+
// The rest config's host field is artificially used to differentiate between the wrappers
91+
require.Equal(t, saAccountWrapper, cfg.Host)
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package authentication_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8+
9+
ocv1 "github.com/operator-framework/operator-controller/api/v1"
10+
"github.com/operator-framework/operator-controller/internal/operator-controller/authentication"
11+
)
12+
13+
func TestSyntheticUserName(t *testing.T) {
14+
syntheticUserName := authentication.SyntheticUserName(ocv1.ClusterExtension{
15+
ObjectMeta: metav1.ObjectMeta{
16+
Name: "my-ext",
17+
},
18+
})
19+
require.Equal(t, "olmv1:clusterextensions:my-ext:admin", syntheticUserName)
20+
}
21+
22+
func TestSyntheticGroups(t *testing.T) {
23+
syntheticGroups := authentication.SyntheticGroups(ocv1.ClusterExtension{})
24+
require.Equal(t, []string{
25+
"olmv1:clusterextensions:admin",
26+
"system:authenticated",
27+
}, syntheticGroups)
28+
}
29+
30+
func TestSyntheticImpersonationConfig(t *testing.T) {
31+
config := authentication.SyntheticImpersonationConfig(ocv1.ClusterExtension{
32+
ObjectMeta: metav1.ObjectMeta{
33+
Name: "my-ext",
34+
},
35+
})
36+
require.Equal(t, "olmv1:clusterextensions:my-ext:admin", config.UserName)
37+
require.Equal(t, []string{
38+
"olmv1:clusterextensions:admin",
39+
"system:authenticated",
40+
}, config.Groups)
41+
require.Empty(t, config.UID)
42+
require.Empty(t, config.Extra)
43+
}

0 commit comments

Comments
 (0)