diff --git a/Taskfile.yaml b/Taskfile.yaml index 7f81e10a..86d9e926 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -8,7 +8,7 @@ vars: CRD_DIRECTORY: config/crd/bases KCP_APIGEN_VERSION: v0.21.0 KCP_VERSION: 0.28.3 - GOLANGCI_LINT_VERSION: v2.5.0 + GOLANGCI_LINT_VERSION: v2.8.0 GOARCH: sh: go env GOARCH GOOS: diff --git a/api/v1alpha1/authorizationmodel_types.go b/api/v1alpha1/authorizationmodel_types.go index 1433b828..a7a53982 100644 --- a/api/v1alpha1/authorizationmodel_types.go +++ b/api/v1alpha1/authorizationmodel_types.go @@ -7,8 +7,12 @@ import ( ) type WorkspaceStoreRef struct { - Name string `json:"name"` - Path string `json:"path"` + Name string `json:"name"` + Cluster string `json:"cluster"` + // Path is deprecated. Use Cluster instead. + // +kubebuilder:validation:Optional + // +kubebuilder:deprecatedversion:warning="v1alpha1" + Path string `json:"path,omitempty"` } // AuthorizationModelSpec defines the desired state of AuthorizationModel. diff --git a/cmd/initializer.go b/cmd/initializer.go index 669124a0..63a86d5d 100644 --- a/cmd/initializer.go +++ b/cmd/initializer.go @@ -79,7 +79,7 @@ var initializerCmd = &cobra.Command{ utilruntime.Must(sourcev1.AddToScheme(runtimeScheme)) utilruntime.Must(helmv2.AddToScheme(runtimeScheme)) - orgClient, err := logicalClusterClientFromKey(mgr.GetLocalManager(), log)(logicalcluster.Name("root:orgs")) + orgClient, err := logicalClusterClientFromKey(mgr.GetLocalManager().GetConfig(), log)(logicalcluster.Name("root:orgs")) if err != nil { setupLog.Error(err, "Failed to create org client") os.Exit(1) diff --git a/cmd/operator.go b/cmd/operator.go index 5211275e..bc969553 100644 --- a/cmd/operator.go +++ b/cmd/operator.go @@ -44,9 +44,9 @@ var ( type NewLogicalClusterClientFunc func(clusterKey logicalcluster.Name) (client.Client, error) -func logicalClusterClientFromKey(mgr ctrl.Manager, log *logger.Logger) NewLogicalClusterClientFunc { +func logicalClusterClientFromKey(config *rest.Config, log *logger.Logger) NewLogicalClusterClientFunc { return func(clusterKey logicalcluster.Name) (client.Client, error) { - cfg := rest.CopyConfig(mgr.GetConfig()) + cfg := rest.CopyConfig(config) parsed, err := url.Parse(cfg.Host) if err != nil { @@ -78,6 +78,13 @@ var operatorCmd = &cobra.Command{ return err } + if operatorCfg.MigrateAuthorizationModels { + if err := migrateAuthorizationModels(ctx, restCfg, scheme, logicalClusterClientFromKey(restCfg, log)); err != nil { + log.Error().Err(err).Msg("migration failed") + return err + } + } + if defaultCfg.Sentry.Dsn != "" { err := sentry.Start(ctx, defaultCfg.Sentry.Dsn, defaultCfg.Environment, defaultCfg.Region, @@ -140,7 +147,7 @@ var operatorCmd = &cobra.Command{ return err } - orgClient, err := logicalClusterClientFromKey(mgr.GetLocalManager(), log)(logicalcluster.Name("root:orgs")) + orgClient, err := logicalClusterClientFromKey(mgr.GetLocalManager().GetConfig(), log)(logicalcluster.Name("root:orgs")) if err != nil { log.Error().Err(err).Msg("Failed to create org client") return err @@ -193,9 +200,50 @@ var operatorCmd = &cobra.Command{ }, } +// this function can be removed after the operator has migrated the authz models in all environments +func migrateAuthorizationModels(ctx context.Context, config *rest.Config, scheme *runtime.Scheme, getClusterClient NewLogicalClusterClientFunc) error { + allClient, err := controller.GetAllClient(config, scheme) + if err != nil { + return fmt.Errorf("failed to create all-cluster client: %w", err) + } + + var models corev1alpha1.AuthorizationModelList + if err := allClient.List(ctx, &models); err != nil { + return fmt.Errorf("failed to list AuthorizationModels: %w", err) + } + + for i := range models.Items { + item := &models.Items[i] + + if item.Spec.StoreRef.Cluster != "" { + continue + } + + if item.Spec.StoreRef.Path == "" { + return fmt.Errorf("AuthorizationModel %s has empty cluster field and no path field to migrate from", item.GetName()) + } + + clusterName := logicalcluster.From(item) + clusterClient, err := getClusterClient(clusterName) + if err != nil { + return fmt.Errorf("failed to create cluster client for AuthorizationModel %s (cluster %s): %w", item.GetName(), clusterName, err) + } + + original := item.DeepCopy() + item.Spec.StoreRef.Cluster = item.Spec.StoreRef.Path + + patch := client.MergeFrom(original) + if err := clusterClient.Patch(ctx, item, patch); err != nil { + return fmt.Errorf("failed to patch AuthorizationModel %s: %w", item.GetName(), err) + } + } + + log.Info().Msg("AuthorizationModel migration completed") + return nil +} + func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(kcptenancyv1alphav1.AddToScheme(scheme)) utilruntime.Must(corev1alpha1.AddToScheme(scheme)) utilruntime.Must(apisv1alpha1.AddToScheme(scheme)) diff --git a/config/crd/bases/core.platform-mesh.io_authorizationmodels.yaml b/config/crd/bases/core.platform-mesh.io_authorizationmodels.yaml index 83e23828..548e7c4a 100644 --- a/config/crd/bases/core.platform-mesh.io_authorizationmodels.yaml +++ b/config/crd/bases/core.platform-mesh.io_authorizationmodels.yaml @@ -44,13 +44,16 @@ spec: type: string storeRef: properties: + cluster: + type: string name: type: string path: + description: Path is deprecated. Use Cluster instead. type: string required: + - cluster - name - - path type: object tuples: items: diff --git a/config/resources/apiexport-core.platform-mesh.io.yaml b/config/resources/apiexport-core.platform-mesh.io.yaml index 94f95832..cbc4609b 100644 --- a/config/resources/apiexport-core.platform-mesh.io.yaml +++ b/config/resources/apiexport-core.platform-mesh.io.yaml @@ -5,8 +5,8 @@ metadata: name: core.platform-mesh.io spec: latestResourceSchemas: - - v250718-a64f278.authorizationmodels.core.platform-mesh.io - v250718-a64f278.stores.core.platform-mesh.io - v251007-1d3512f.invites.core.platform-mesh.io - v251215-8997bd4.identityproviderconfigurations.core.platform-mesh.io + - v260112-5925c7e.authorizationmodels.core.platform-mesh.io status: {} diff --git a/config/resources/apiresourceschema-authorizationmodels.core.platform-mesh.io.yaml b/config/resources/apiresourceschema-authorizationmodels.core.platform-mesh.io.yaml index e405e39b..7a5c4011 100644 --- a/config/resources/apiresourceschema-authorizationmodels.core.platform-mesh.io.yaml +++ b/config/resources/apiresourceschema-authorizationmodels.core.platform-mesh.io.yaml @@ -2,7 +2,7 @@ apiVersion: apis.kcp.io/v1alpha1 kind: APIResourceSchema metadata: creationTimestamp: null - name: v250718-a64f278.authorizationmodels.core.platform-mesh.io + name: v260112-5925c7e.authorizationmodels.core.platform-mesh.io spec: group: core.platform-mesh.io names: @@ -40,13 +40,16 @@ spec: type: string storeRef: properties: + cluster: + type: string name: type: string path: + description: Path is deprecated. Use Cluster instead. type: string required: + - cluster - name - - path type: object tuples: items: diff --git a/internal/config/config.go b/internal/config/config.go index 7d9f58a3..411aebb7 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -22,6 +22,7 @@ type Config struct { DevelopmentAllowUnverifiedEmails bool `mapstructure:"development-allow-unverified-emails" default:"false"` InitializerName string `mapstructure:"initializer-name" default:"root:security"` DomainCALookup bool `mapstructure:"domain-ca-lookup" default:"false"` + MigrateAuthorizationModels bool `mapstructure:"migrate-authorization-models" default:"false"` HttpClientTimeoutSeconds int `mapstructure:"http-client-timeout-seconds" default:"30"` IDP struct { // SMTP settings diff --git a/internal/controller/apibinding_controller.go b/internal/controller/apibinding_controller.go index 692dc7aa..a0e7ad19 100644 --- a/internal/controller/apibinding_controller.go +++ b/internal/controller/apibinding_controller.go @@ -19,14 +19,15 @@ import ( mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager" mcreconcile "sigs.k8s.io/multicluster-runtime/pkg/reconcile" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/rest" kcpv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1" "github.com/kcp-dev/logicalcluster/v3" ) -func getAllClient(mcMgr mcmanager.Manager) (client.Client, error) { - allCfg := rest.CopyConfig(mcMgr.GetLocalManager().GetConfig()) +func GetAllClient(config *rest.Config, schema *runtime.Scheme) (client.Client, error) { + allCfg := rest.CopyConfig(config) parsed, err := url.Parse(allCfg.Host) if err != nil { @@ -47,7 +48,7 @@ func getAllClient(mcMgr mcmanager.Manager) (client.Client, error) { log.Info().Str("host", allCfg.Host).Msg("using host") allClient, err := client.New(allCfg, client.Options{ - Scheme: mcMgr.GetLocalManager().GetScheme(), + Scheme: schema, }) if err != nil { return nil, err @@ -56,7 +57,7 @@ func getAllClient(mcMgr mcmanager.Manager) (client.Client, error) { } func NewAPIBindingReconciler(logger *logger.Logger, mcMgr mcmanager.Manager) *APIBindingReconciler { - allclient, err := getAllClient(mcMgr) + allclient, err := GetAllClient(mcMgr.GetLocalManager().GetConfig(), mcMgr.GetLocalManager().GetScheme()) if err != nil { log.Fatal().Err(err).Msg("unable to create new client") } diff --git a/internal/controller/store_controller.go b/internal/controller/store_controller.go index eeb44dc8..b43f5737 100644 --- a/internal/controller/store_controller.go +++ b/internal/controller/store_controller.go @@ -34,7 +34,7 @@ type StoreReconciler struct { } func NewStoreReconciler(log *logger.Logger, fga openfgav1.OpenFGAServiceClient, mcMgr mcmanager.Manager) *StoreReconciler { - allClient, err := getAllClient(mcMgr) + allClient, err := GetAllClient(mcMgr.GetLocalManager().GetConfig(), mcMgr.GetLocalManager().GetScheme()) if err != nil { log.Fatal().Err(err).Msg("unable to create new client") } @@ -80,7 +80,7 @@ func (r *StoreReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platforme Name: model.Spec.StoreRef.Name, }, }, - ClusterName: model.Spec.StoreRef.Path, + ClusterName: model.Spec.StoreRef.Cluster, }, } }), diff --git a/internal/subroutine/authorization_model.go b/internal/subroutine/authorization_model.go index e5f265e9..8020da79 100644 --- a/internal/subroutine/authorization_model.go +++ b/internal/subroutine/authorization_model.go @@ -14,7 +14,7 @@ import ( "github.com/platform-mesh/golang-commons/controller/lifecycle/subroutine" "github.com/platform-mesh/golang-commons/errors" "github.com/platform-mesh/golang-commons/logger" - "github.com/platform-mesh/security-operator/api/v1alpha1" + securityv1alpha1 "github.com/platform-mesh/security-operator/api/v1alpha1" "google.golang.org/protobuf/encoding/protojson" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -100,23 +100,23 @@ func (a *authorizationModelSubroutine) Finalize(ctx context.Context, instance ru return ctrl.Result{}, nil } -func getRelatedAuthorizationModels(ctx context.Context, k8s client.Client, store *v1alpha1.Store) (v1alpha1.AuthorizationModelList, error) { +func getRelatedAuthorizationModels(ctx context.Context, k8s client.Client, store *securityv1alpha1.Store) (securityv1alpha1.AuthorizationModelList, error) { storeClusterKey, ok := mccontext.ClusterFrom(ctx) if !ok { - return v1alpha1.AuthorizationModelList{}, fmt.Errorf("unable to get cluster key from context") + return securityv1alpha1.AuthorizationModelList{}, fmt.Errorf("unable to get cluster key from context") } allCtx := mccontext.WithCluster(ctx, "") - allAuthorizationModels := v1alpha1.AuthorizationModelList{} + allAuthorizationModels := securityv1alpha1.AuthorizationModelList{} if err := k8s.List(allCtx, &allAuthorizationModels); err != nil { - return v1alpha1.AuthorizationModelList{}, err + return securityv1alpha1.AuthorizationModelList{}, err } - var extendingModules v1alpha1.AuthorizationModelList + var extendingModules securityv1alpha1.AuthorizationModelList for _, model := range allAuthorizationModels.Items { - if model.Spec.StoreRef.Name != store.Name || model.Spec.StoreRef.Path != storeClusterKey { + if model.Spec.StoreRef.Name != store.Name || model.Spec.StoreRef.Cluster != storeClusterKey { continue } @@ -128,7 +128,7 @@ func getRelatedAuthorizationModels(ctx context.Context, k8s client.Client, store func (a *authorizationModelSubroutine) Process(ctx context.Context, instance runtimeobject.RuntimeObject) (reconcile.Result, errors.OperatorError) { log := logger.LoadLoggerFromContext(ctx) - store := instance.(*v1alpha1.Store) + store := instance.(*securityv1alpha1.Store) extendingModules, err := getRelatedAuthorizationModels(ctx, a.allClient, store) if err != nil { diff --git a/internal/subroutine/authorization_model_generation.go b/internal/subroutine/authorization_model_generation.go index 0c386c6e..235fbfd2 100644 --- a/internal/subroutine/authorization_model_generation.go +++ b/internal/subroutine/authorization_model_generation.go @@ -268,8 +268,8 @@ func (a *AuthorizationModelGenerationSubroutine) Process(ctx context.Context, in model.Spec = securityv1alpha1.AuthorizationModelSpec{ Model: buffer.String(), StoreRef: securityv1alpha1.WorkspaceStoreRef{ - Name: accountInfo.Spec.Organization.Name, - Path: accountInfo.Spec.Organization.OriginClusterId, + Name: accountInfo.Spec.Organization.Name, + Cluster: accountInfo.Spec.Organization.OriginClusterId, }, } return nil diff --git a/internal/subroutine/authorization_model_test.go b/internal/subroutine/authorization_model_test.go index 41829dd4..83a642c4 100644 --- a/internal/subroutine/authorization_model_test.go +++ b/internal/subroutine/authorization_model_test.go @@ -8,7 +8,7 @@ import ( language "github.com/openfga/language/pkg/go/transformer" "github.com/platform-mesh/golang-commons/errors" "github.com/platform-mesh/golang-commons/logger/testlogger" - "github.com/platform-mesh/security-operator/api/v1alpha1" + securityv1alpha1 "github.com/platform-mesh/security-operator/api/v1alpha1" "github.com/platform-mesh/security-operator/internal/subroutine" "github.com/platform-mesh/security-operator/internal/subroutine/mocks" "github.com/stretchr/testify/assert" @@ -100,7 +100,7 @@ func TestAuthorizationModelFinalize(t *testing.T) { func TestAuthorizationModelProcess(t *testing.T) { tests := []struct { name string - store *v1alpha1.Store + store *securityv1alpha1.Store fgaMocks func(*mocks.MockOpenFGAServiceClient) k8sMocks func(*mocks.MockClient) mgrMocks func(*mocks.MockManager) @@ -108,28 +108,28 @@ func TestAuthorizationModelProcess(t *testing.T) { }{ { name: "should reconcile and apply the merged model", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Spec: v1alpha1.StoreSpec{ + Spec: securityv1alpha1.StoreSpec{ CoreModule: coreModule, }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", }, }, k8sMocks: func(k8s *mocks.MockClient) { k8s.EXPECT().List(mock.Anything, mock.Anything).RunAndReturn( func(ctx context.Context, ol client.ObjectList, lo ...client.ListOption) error { - am := ol.(*v1alpha1.AuthorizationModelList) - am.Items = []v1alpha1.AuthorizationModel{ + am := ol.(*securityv1alpha1.AuthorizationModelList) + am.Items = []securityv1alpha1.AuthorizationModel{ { - Spec: v1alpha1.AuthorizationModelSpec{ + Spec: securityv1alpha1.AuthorizationModelSpec{ Model: extensionModel, - StoreRef: v1alpha1.WorkspaceStoreRef{ - Name: "store", - Path: "path", + StoreRef: securityv1alpha1.WorkspaceStoreRef{ + Name: "store", + Cluster: "path", }, }, }, @@ -164,14 +164,14 @@ func TestAuthorizationModelProcess(t *testing.T) { }, { name: "should reconcile and not patch the model in case they are equal", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Spec: v1alpha1.StoreSpec{ + Spec: securityv1alpha1.StoreSpec{ CoreModule: coreModule, }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", AuthorizationModelID: "id", }, @@ -179,14 +179,14 @@ func TestAuthorizationModelProcess(t *testing.T) { k8sMocks: func(k8s *mocks.MockClient) { k8s.EXPECT().List(mock.Anything, mock.Anything).RunAndReturn( func(ctx context.Context, ol client.ObjectList, lo ...client.ListOption) error { - am := ol.(*v1alpha1.AuthorizationModelList) - am.Items = []v1alpha1.AuthorizationModel{ + am := ol.(*securityv1alpha1.AuthorizationModelList) + am.Items = []securityv1alpha1.AuthorizationModel{ { - Spec: v1alpha1.AuthorizationModelSpec{ + Spec: securityv1alpha1.AuthorizationModelSpec{ Model: extensionModel, - StoreRef: v1alpha1.WorkspaceStoreRef{ - Name: "store", - Path: "path", + StoreRef: securityv1alpha1.WorkspaceStoreRef{ + Name: "store", + Cluster: "path", }, }, }, @@ -245,14 +245,14 @@ type core_namespace }, { name: "should stop reconciliation if the authorization model is not found", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Spec: v1alpha1.StoreSpec{ + Spec: securityv1alpha1.StoreSpec{ CoreModule: coreModule, }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", AuthorizationModelID: "id", }, @@ -260,14 +260,14 @@ type core_namespace k8sMocks: func(k8s *mocks.MockClient) { k8s.EXPECT().List(mock.Anything, mock.Anything).RunAndReturn( func(ctx context.Context, ol client.ObjectList, lo ...client.ListOption) error { - am := ol.(*v1alpha1.AuthorizationModelList) - am.Items = []v1alpha1.AuthorizationModel{ + am := ol.(*securityv1alpha1.AuthorizationModelList) + am.Items = []securityv1alpha1.AuthorizationModel{ { - Spec: v1alpha1.AuthorizationModelSpec{ + Spec: securityv1alpha1.AuthorizationModelSpec{ Model: extensionModel, - StoreRef: v1alpha1.WorkspaceStoreRef{ - Name: "store", - Path: "path", + StoreRef: securityv1alpha1.WorkspaceStoreRef{ + Name: "store", + Cluster: "path", }, }, }, @@ -283,25 +283,25 @@ type core_namespace }, { name: "should stop reconciliation for invalid model", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", }, }, k8sMocks: func(k8s *mocks.MockClient) { k8s.EXPECT().List(mock.Anything, mock.Anything).RunAndReturn( func(ctx context.Context, ol client.ObjectList, lo ...client.ListOption) error { - am := ol.(*v1alpha1.AuthorizationModelList) - am.Items = []v1alpha1.AuthorizationModel{ + am := ol.(*securityv1alpha1.AuthorizationModelList) + am.Items = []securityv1alpha1.AuthorizationModel{ { - Spec: v1alpha1.AuthorizationModelSpec{ + Spec: securityv1alpha1.AuthorizationModelSpec{ Model: extensionModel, - StoreRef: v1alpha1.WorkspaceStoreRef{ - Name: "store", - Path: "path", + StoreRef: securityv1alpha1.WorkspaceStoreRef{ + Name: "store", + Cluster: "path", }, }, }, @@ -314,28 +314,28 @@ type core_namespace }, { name: "should stop reconciliation for failing write", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Spec: v1alpha1.StoreSpec{ + Spec: securityv1alpha1.StoreSpec{ CoreModule: coreModule, }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", }, }, k8sMocks: func(k8s *mocks.MockClient) { k8s.EXPECT().List(mock.Anything, mock.Anything).RunAndReturn( func(ctx context.Context, ol client.ObjectList, lo ...client.ListOption) error { - am := ol.(*v1alpha1.AuthorizationModelList) - am.Items = []v1alpha1.AuthorizationModel{ + am := ol.(*securityv1alpha1.AuthorizationModelList) + am.Items = []securityv1alpha1.AuthorizationModel{ { - Spec: v1alpha1.AuthorizationModelSpec{ + Spec: securityv1alpha1.AuthorizationModelSpec{ Model: extensionModel, - StoreRef: v1alpha1.WorkspaceStoreRef{ - Name: "store", - Path: "path", + StoreRef: securityv1alpha1.WorkspaceStoreRef{ + Name: "store", + Cluster: "path", }, }, }, diff --git a/internal/subroutine/store_test.go b/internal/subroutine/store_test.go index 550c94a8..6f177138 100644 --- a/internal/subroutine/store_test.go +++ b/internal/subroutine/store_test.go @@ -6,7 +6,7 @@ import ( "testing" openfgav1 "github.com/openfga/api/proto/openfga/v1" - "github.com/platform-mesh/security-operator/api/v1alpha1" + securityv1alpha1 "github.com/platform-mesh/security-operator/api/v1alpha1" "github.com/platform-mesh/security-operator/internal/subroutine" "github.com/platform-mesh/security-operator/internal/subroutine/mocks" "github.com/stretchr/testify/assert" @@ -34,7 +34,7 @@ func TestFinalizers(t *testing.T) { func TestProcess(t *testing.T) { tests := []struct { name string - store *v1alpha1.Store + store *securityv1alpha1.Store fgaMocks func(*mocks.MockOpenFGAServiceClient) k8sMocks func(*mocks.MockClient) mgrMocks func(*mocks.MockManager) @@ -42,7 +42,7 @@ func TestProcess(t *testing.T) { }{ { name: "should try and create the store if it does not exist", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, @@ -54,7 +54,7 @@ func TestProcess(t *testing.T) { }, { name: "should skip creation if the store already exists", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, @@ -71,11 +71,11 @@ func TestProcess(t *testing.T) { }, { name: "should verify the store if .status.storeId is set", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", }, }, @@ -85,11 +85,11 @@ func TestProcess(t *testing.T) { }, { name: "should verify and update the store if .status.storeId is set but name differs", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", }, }, @@ -100,7 +100,7 @@ func TestProcess(t *testing.T) { }, { name: "should fail if store listing fails", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, @@ -112,7 +112,7 @@ func TestProcess(t *testing.T) { }, { name: "should fail if store creation fails", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, @@ -125,11 +125,11 @@ func TestProcess(t *testing.T) { }, { name: "should fail if get store fails when verifying existing store", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", }, }, @@ -140,11 +140,11 @@ func TestProcess(t *testing.T) { }, { name: "should fail if update store fails when names differ", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", }, }, @@ -180,7 +180,7 @@ func TestProcess(t *testing.T) { func TestFinalize(t *testing.T) { tests := []struct { name string - store *v1alpha1.Store + store *securityv1alpha1.Store fgaMocks func(*mocks.MockOpenFGAServiceClient) k8sMocks func(*mocks.MockClient) mgrMocks func(*mocks.MockManager) @@ -188,7 +188,7 @@ func TestFinalize(t *testing.T) { }{ { name: "should skip reconciliation if .status.storeId is not set", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, @@ -196,23 +196,23 @@ func TestFinalize(t *testing.T) { }, { name: "should deny deletion if at least authorizationModel is referencing the store", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", }, }, k8sMocks: func(k8s *mocks.MockClient) { k8s.EXPECT().List(mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, ol client.ObjectList, lo ...client.ListOption) error { - if list, ok := ol.(*v1alpha1.AuthorizationModelList); ok { - list.Items = []v1alpha1.AuthorizationModel{ + if list, ok := ol.(*securityv1alpha1.AuthorizationModelList); ok { + list.Items = []securityv1alpha1.AuthorizationModel{ { - Spec: v1alpha1.AuthorizationModelSpec{ - StoreRef: v1alpha1.WorkspaceStoreRef{ - Name: "store", - Path: "path", + Spec: securityv1alpha1.AuthorizationModelSpec{ + StoreRef: securityv1alpha1.WorkspaceStoreRef{ + Name: "store", + Cluster: "path", }, }, }, @@ -225,11 +225,11 @@ func TestFinalize(t *testing.T) { }, { name: "should deny deletion the list call is failing", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", }, }, @@ -240,11 +240,11 @@ func TestFinalize(t *testing.T) { }, { name: "should delete the store if no authorizationModel is referencing it", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", }, }, @@ -257,11 +257,11 @@ func TestFinalize(t *testing.T) { }, { name: "should reconcile successfully if store is not found with the .status.storeId", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", }, }, @@ -274,11 +274,11 @@ func TestFinalize(t *testing.T) { }, { name: "should not reconcile successfully deletion is errorneous", - store: &v1alpha1.Store{ + store: &securityv1alpha1.Store{ ObjectMeta: metav1.ObjectMeta{ Name: "store", }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "id", }, }, diff --git a/internal/subroutine/tuples.go b/internal/subroutine/tuples.go index 91eb1ee0..c6dfc2a3 100644 --- a/internal/subroutine/tuples.go +++ b/internal/subroutine/tuples.go @@ -11,7 +11,7 @@ import ( "github.com/platform-mesh/golang-commons/errors" "github.com/platform-mesh/golang-commons/fga/helpers" "github.com/platform-mesh/golang-commons/logger" - "github.com/platform-mesh/security-operator/api/v1alpha1" + securityv1alpha1 "github.com/platform-mesh/security-operator/api/v1alpha1" ctrl "sigs.k8s.io/controller-runtime" mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager" @@ -29,22 +29,22 @@ func (t *tupleSubroutine) Finalize(ctx context.Context, instance runtimeobject.R var storeID string var authorizationModelID string - var managedTuples []v1alpha1.Tuple + var managedTuples []securityv1alpha1.Tuple switch obj := instance.(type) { - case *v1alpha1.Store: + case *securityv1alpha1.Store: storeID = obj.Status.StoreID authorizationModelID = obj.Status.AuthorizationModelID managedTuples = obj.Status.ManagedTuples - case *v1alpha1.AuthorizationModel: + case *securityv1alpha1.AuthorizationModel: managedTuples = obj.Status.ManagedTuples - storeCluster, err := t.mgr.GetCluster(ctx, obj.Spec.StoreRef.Path) + storeCluster, err := t.mgr.GetCluster(ctx, obj.Spec.StoreRef.Cluster) if err != nil { return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("unable to get store cluster: %w", err), true, false) } - var store v1alpha1.Store + var store securityv1alpha1.Store err = storeCluster.GetClient().Get(ctx, types.NamespacedName{ Name: obj.Spec.StoreRef.Name, }, &store) @@ -80,9 +80,9 @@ func (t *tupleSubroutine) Finalize(ctx context.Context, instance runtimeobject.R } switch obj := instance.(type) { - case *v1alpha1.Store: + case *securityv1alpha1.Store: obj.Status.ManagedTuples = nil - case *v1alpha1.AuthorizationModel: + case *securityv1alpha1.AuthorizationModel: obj.Status.ManagedTuples = nil } @@ -103,26 +103,26 @@ func (t *tupleSubroutine) Process(ctx context.Context, instance runtimeobject.Ru var storeID string var authorizationModelID string - var specTuples []v1alpha1.Tuple - var managedTuples []v1alpha1.Tuple + var specTuples []securityv1alpha1.Tuple + var managedTuples []securityv1alpha1.Tuple switch obj := instance.(type) { - case *v1alpha1.Store: + case *securityv1alpha1.Store: storeID = obj.Status.StoreID authorizationModelID = obj.Status.AuthorizationModelID specTuples = obj.Spec.Tuples managedTuples = obj.Status.ManagedTuples - case *v1alpha1.AuthorizationModel: + case *securityv1alpha1.AuthorizationModel: specTuples = obj.Spec.Tuples managedTuples = obj.Status.ManagedTuples - storeCluster, err := t.mgr.GetCluster(ctx, obj.Spec.StoreRef.Path) + storeCluster, err := t.mgr.GetCluster(ctx, obj.Spec.StoreRef.Cluster) if err != nil { return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("unable to get store cluster: %w", err), true, false) } - var store v1alpha1.Store + var store securityv1alpha1.Store err = storeCluster.GetClient().Get(ctx, types.NamespacedName{ Name: obj.Spec.StoreRef.Name, }, &store) @@ -158,7 +158,7 @@ func (t *tupleSubroutine) Process(ctx context.Context, instance runtimeobject.Ru } for _, tuple := range managedTuples { - if idx := slices.IndexFunc(specTuples, func(t v1alpha1.Tuple) bool { + if idx := slices.IndexFunc(specTuples, func(t securityv1alpha1.Tuple) bool { return t.Object == tuple.Object && t.Relation == tuple.Relation && t.User == tuple.User }); idx != -1 { continue @@ -188,9 +188,9 @@ func (t *tupleSubroutine) Process(ctx context.Context, instance runtimeobject.Ru } switch obj := instance.(type) { - case *v1alpha1.Store: + case *securityv1alpha1.Store: obj.Status.ManagedTuples = specTuples - case *v1alpha1.AuthorizationModel: + case *securityv1alpha1.AuthorizationModel: obj.Status.ManagedTuples = specTuples } diff --git a/internal/subroutine/tuples_test.go b/internal/subroutine/tuples_test.go index e38ae105..fe53d42c 100644 --- a/internal/subroutine/tuples_test.go +++ b/internal/subroutine/tuples_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - "github.com/platform-mesh/security-operator/api/v1alpha1" + securityv1alpha1 "github.com/platform-mesh/security-operator/api/v1alpha1" "github.com/platform-mesh/security-operator/internal/subroutine" "github.com/platform-mesh/security-operator/internal/subroutine/mocks" "github.com/stretchr/testify/assert" @@ -29,7 +29,7 @@ func TestTupleFinalizers(t *testing.T) { func TestTupleProcessWithStore(t *testing.T) { tests := []struct { name string - store *v1alpha1.Store + store *securityv1alpha1.Store fgaMocks func(*mocks.MockOpenFGAServiceClient) k8sMocks func(*mocks.MockClient) mgrMocks func(*mocks.MockManager) @@ -37,9 +37,9 @@ func TestTupleProcessWithStore(t *testing.T) { }{ { name: "should process and add tuples to the store", - store: &v1alpha1.Store{ - Spec: v1alpha1.StoreSpec{ - Tuples: []v1alpha1.Tuple{ + store: &securityv1alpha1.Store{ + Spec: securityv1alpha1.StoreSpec{ + Tuples: []securityv1alpha1.Tuple{ { Object: "foo", Relation: "bar", @@ -57,10 +57,10 @@ func TestTupleProcessWithStore(t *testing.T) { }, }, }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "store-id", AuthorizationModelID: "auth-model-id", - ManagedTuples: []v1alpha1.Tuple{ + ManagedTuples: []securityv1alpha1.Tuple{ { Object: "foo", Relation: "bar", @@ -75,9 +75,9 @@ func TestTupleProcessWithStore(t *testing.T) { }, { name: "should process and add/remove tuples to the store", - store: &v1alpha1.Store{ - Spec: v1alpha1.StoreSpec{ - Tuples: []v1alpha1.Tuple{ + store: &securityv1alpha1.Store{ + Spec: securityv1alpha1.StoreSpec{ + Tuples: []securityv1alpha1.Tuple{ { Object: "foo", Relation: "bar", @@ -95,10 +95,10 @@ func TestTupleProcessWithStore(t *testing.T) { }, }, }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "store-id", AuthorizationModelID: "auth-model-id", - ManagedTuples: []v1alpha1.Tuple{ + ManagedTuples: []securityv1alpha1.Tuple{ { Object: "foo", Relation: "bar", @@ -117,9 +117,9 @@ func TestTupleProcessWithStore(t *testing.T) { }, { name: "should stop processing if an error occurs", - store: &v1alpha1.Store{ - Spec: v1alpha1.StoreSpec{ - Tuples: []v1alpha1.Tuple{ + store: &securityv1alpha1.Store{ + Spec: securityv1alpha1.StoreSpec{ + Tuples: []securityv1alpha1.Tuple{ { Object: "foo", Relation: "bar", @@ -137,10 +137,10 @@ func TestTupleProcessWithStore(t *testing.T) { }, }, }, - Status: v1alpha1.StoreStatus{ + Status: securityv1alpha1.StoreStatus{ StoreID: "store-id", AuthorizationModelID: "auth-model-id", - ManagedTuples: []v1alpha1.Tuple{ + ManagedTuples: []securityv1alpha1.Tuple{ { Object: "foo", Relation: "bar", @@ -184,7 +184,7 @@ func TestTupleProcessWithStore(t *testing.T) { func TestTupleProcessWithAuthorizationModel(t *testing.T) { tests := []struct { name string - store *v1alpha1.AuthorizationModel + store *securityv1alpha1.AuthorizationModel fgaMocks func(*mocks.MockOpenFGAServiceClient) k8sMocks func(*mocks.MockClient) mgrMocks func(*mocks.MockManager) @@ -192,18 +192,18 @@ func TestTupleProcessWithAuthorizationModel(t *testing.T) { }{ { name: "should process and add tuples to the authorization model", - store: &v1alpha1.AuthorizationModel{ + store: &securityv1alpha1.AuthorizationModel{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ - v1alpha1.StoreRefLabelKey: "store", + securityv1alpha1.StoreRefLabelKey: "store", }, }, - Spec: v1alpha1.AuthorizationModelSpec{ - StoreRef: v1alpha1.WorkspaceStoreRef{ - Name: "store", - Path: "store-cluster", + Spec: securityv1alpha1.AuthorizationModelSpec{ + StoreRef: securityv1alpha1.WorkspaceStoreRef{ + Name: "store", + Cluster: "store-cluster", }, - Tuples: []v1alpha1.Tuple{ + Tuples: []securityv1alpha1.Tuple{ { Object: "foo", Relation: "bar", @@ -234,9 +234,9 @@ func TestTupleProcessWithAuthorizationModel(t *testing.T) { mgr.EXPECT().GetCluster(mock.Anything, "store-cluster").Return(storeCluster, nil) storeCluster.EXPECT().GetClient().Return(storeClient) storeClient.EXPECT().Get(mock.Anything, mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, nn types.NamespacedName, o client.Object, opts ...client.GetOption) error { - store := o.(*v1alpha1.Store) - *store = v1alpha1.Store{ - Status: v1alpha1.StoreStatus{ + store := o.(*securityv1alpha1.Store) + *store = securityv1alpha1.Store{ + Status: securityv1alpha1.StoreStatus{ StoreID: "store-id", AuthorizationModelID: "auth-model-id", }, @@ -247,18 +247,18 @@ func TestTupleProcessWithAuthorizationModel(t *testing.T) { }, { name: "should process and add/remove tuples to the authorization model", - store: &v1alpha1.AuthorizationModel{ + store: &securityv1alpha1.AuthorizationModel{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ - v1alpha1.StoreRefLabelKey: "store", + securityv1alpha1.StoreRefLabelKey: "store", }, }, - Spec: v1alpha1.AuthorizationModelSpec{ - StoreRef: v1alpha1.WorkspaceStoreRef{ - Name: "store", - Path: "store-cluster", + Spec: securityv1alpha1.AuthorizationModelSpec{ + StoreRef: securityv1alpha1.WorkspaceStoreRef{ + Name: "store", + Cluster: "store-cluster", }, - Tuples: []v1alpha1.Tuple{ + Tuples: []securityv1alpha1.Tuple{ { Object: "foo", Relation: "bar", @@ -276,8 +276,8 @@ func TestTupleProcessWithAuthorizationModel(t *testing.T) { }, }, }, - Status: v1alpha1.AuthorizationModelStatus{ - ManagedTuples: []v1alpha1.Tuple{ + Status: securityv1alpha1.AuthorizationModelStatus{ + ManagedTuples: []securityv1alpha1.Tuple{ { Object: "foo", Relation: "bar", @@ -302,9 +302,9 @@ func TestTupleProcessWithAuthorizationModel(t *testing.T) { mgr.EXPECT().GetCluster(mock.Anything, "store-cluster").Return(storeCluster, nil) storeCluster.EXPECT().GetClient().Return(storeClient) storeClient.EXPECT().Get(mock.Anything, mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, nn types.NamespacedName, o client.Object, opts ...client.GetOption) error { - store := o.(*v1alpha1.Store) - *store = v1alpha1.Store{ - Status: v1alpha1.StoreStatus{ + store := o.(*securityv1alpha1.Store) + *store = securityv1alpha1.Store{ + Status: securityv1alpha1.StoreStatus{ StoreID: "store-id", AuthorizationModelID: "auth-model-id", }, @@ -348,7 +348,7 @@ func TestTupleProcessWithAuthorizationModel(t *testing.T) { func TestTupleFinalizationWithAuthorizationModel(t *testing.T) { tests := []struct { name string - store *v1alpha1.AuthorizationModel + store *securityv1alpha1.AuthorizationModel fgaMocks func(*mocks.MockOpenFGAServiceClient) k8sMocks func(*mocks.MockClient) mgrMocks func(*mocks.MockManager) @@ -356,20 +356,20 @@ func TestTupleFinalizationWithAuthorizationModel(t *testing.T) { }{ { name: "should finalize the authorization model", - store: &v1alpha1.AuthorizationModel{ + store: &securityv1alpha1.AuthorizationModel{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ - v1alpha1.StoreRefLabelKey: "store", + securityv1alpha1.StoreRefLabelKey: "store", }, }, - Spec: v1alpha1.AuthorizationModelSpec{ - StoreRef: v1alpha1.WorkspaceStoreRef{ - Name: "store", - Path: "store-cluster", + Spec: securityv1alpha1.AuthorizationModelSpec{ + StoreRef: securityv1alpha1.WorkspaceStoreRef{ + Name: "store", + Cluster: "store-cluster", }, }, - Status: v1alpha1.AuthorizationModelStatus{ - ManagedTuples: []v1alpha1.Tuple{ + Status: securityv1alpha1.AuthorizationModelStatus{ + ManagedTuples: []securityv1alpha1.Tuple{ { Object: "foo", Relation: "bar", @@ -391,9 +391,9 @@ func TestTupleFinalizationWithAuthorizationModel(t *testing.T) { mgr.EXPECT().GetCluster(mock.Anything, "store-cluster").Return(storeCluster, nil) storeCluster.EXPECT().GetClient().Return(storeClient) storeClient.EXPECT().Get(mock.Anything, mock.Anything, mock.Anything).RunAndReturn(func(ctx context.Context, nn types.NamespacedName, o client.Object, opts ...client.GetOption) error { - store := o.(*v1alpha1.Store) - *store = v1alpha1.Store{ - Status: v1alpha1.StoreStatus{ + store := o.(*securityv1alpha1.Store) + *store = securityv1alpha1.Store{ + Status: securityv1alpha1.StoreStatus{ StoreID: "store-id", AuthorizationModelID: "auth-model-id", }, @@ -437,7 +437,7 @@ func TestTupleFinalizationWithAuthorizationModel(t *testing.T) { func TestTupleFinalizationWithStore(t *testing.T) { tests := []struct { name string - store *v1alpha1.Store + store *securityv1alpha1.Store fgaMocks func(*mocks.MockOpenFGAServiceClient) k8sMocks func(*mocks.MockClient) mgrMocks func(*mocks.MockManager) @@ -445,9 +445,9 @@ func TestTupleFinalizationWithStore(t *testing.T) { }{ { name: "should finalize the authorization model", - store: &v1alpha1.Store{ - Status: v1alpha1.StoreStatus{ - ManagedTuples: []v1alpha1.Tuple{ + store: &securityv1alpha1.Store{ + Status: securityv1alpha1.StoreStatus{ + ManagedTuples: []securityv1alpha1.Tuple{ { Object: "foo", Relation: "bar", @@ -463,9 +463,9 @@ func TestTupleFinalizationWithStore(t *testing.T) { }, { name: "should stop finalizing the authorization model if an error occurs", - store: &v1alpha1.Store{ - Status: v1alpha1.StoreStatus{ - ManagedTuples: []v1alpha1.Tuple{ + store: &securityv1alpha1.Store{ + Status: securityv1alpha1.StoreStatus{ + ManagedTuples: []securityv1alpha1.Tuple{ { Object: "foo", Relation: "bar", diff --git a/internal/test/integration/authorization_model_generation_test.go b/internal/test/integration/authorization_model_generation_test.go index a173595b..645532ee 100644 --- a/internal/test/integration/authorization_model_generation_test.go +++ b/internal/test/integration/authorization_model_generation_test.go @@ -60,7 +60,7 @@ func (suite *IntegrationSuite) TestAuthorizationModelGeneration_Process() { }, 10*time.Second, 200*time.Millisecond, "authorizationModel should be created by controller") suite.Assert().Equal(testOrgName, model.Spec.StoreRef.Name) - suite.Assert().Equal(testOrgPath.String(), model.Spec.StoreRef.Path) + suite.Assert().Equal(testOrgPath.String(), model.Spec.StoreRef.Cluster) } func (suite *IntegrationSuite) TestAuthorizationModelGeneration_Finalize() { diff --git a/internal/test/integration/yaml/apiexport-core.platform-mesh.io.yaml b/internal/test/integration/yaml/apiexport-core.platform-mesh.io.yaml index d7dde1d9..c3cda57d 100644 --- a/internal/test/integration/yaml/apiexport-core.platform-mesh.io.yaml +++ b/internal/test/integration/yaml/apiexport-core.platform-mesh.io.yaml @@ -7,7 +7,7 @@ spec: latestResourceSchemas: - v250715-5b899f6.accountinfos.core.platform-mesh.io - v250715-5b899f6.accounts.core.platform-mesh.io - - v250718-a64f278.authorizationmodels.core.platform-mesh.io + - v260108-81fc9ff.authorizationmodels.core.platform-mesh.io - v250718-a64f278.stores.core.platform-mesh.io permissionClaims: - resource: apibindings diff --git a/internal/test/integration/yaml/apiresourceschema-authorizationmodels.core.platform-mesh.io.yaml b/internal/test/integration/yaml/apiresourceschema-authorizationmodels.core.platform-mesh.io.yaml index e405e39b..ee09594f 100644 --- a/internal/test/integration/yaml/apiresourceschema-authorizationmodels.core.platform-mesh.io.yaml +++ b/internal/test/integration/yaml/apiresourceschema-authorizationmodels.core.platform-mesh.io.yaml @@ -2,7 +2,7 @@ apiVersion: apis.kcp.io/v1alpha1 kind: APIResourceSchema metadata: creationTimestamp: null - name: v250718-a64f278.authorizationmodels.core.platform-mesh.io + name: v260108-81fc9ff.authorizationmodels.core.platform-mesh.io spec: group: core.platform-mesh.io names: @@ -40,13 +40,13 @@ spec: type: string storeRef: properties: - name: + cluster: type: string - path: + name: type: string required: + - cluster - name - - path type: object tuples: items: