diff --git a/crypto/manager_kmip.go b/crypto/manager_kmip.go new file mode 100644 index 000000000..f52e45ad1 --- /dev/null +++ b/crypto/manager_kmip.go @@ -0,0 +1,423 @@ +/* +Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crypto + +import ( + "context" + "fmt" + + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/types" +) + +type ManagerKmip struct { + object.Common +} + +// GetManagerKmip wraps NewManager, returning ErrNotSupported when the client is +// not connected to a vCenter instance. +func GetManagerKmip(c *vim25.Client) (*ManagerKmip, error) { + if c.ServiceContent.CryptoManager == nil { + return nil, object.ErrNotSupported + } + return NewManagerKmip(c), nil +} + +func NewManagerKmip(c *vim25.Client) *ManagerKmip { + m := ManagerKmip{ + Common: object.NewCommon(c, *c.ServiceContent.CryptoManager), + } + return &m +} + +func (m ManagerKmip) ListKmipServers( + ctx context.Context, + limit *int32) ([]types.KmipClusterInfo, error) { + + req := types.ListKmipServers{ + This: m.Reference(), + Limit: limit, + } + res, err := methods.ListKmipServers(ctx, m.Client(), &req) + if err != nil { + return nil, err + } + return res.Returnval, nil +} + +func (m ManagerKmip) IsDefaultProviderNative( + ctx context.Context, + entity *types.ManagedObjectReference, + defaultsToParent bool) (bool, error) { + + defaultProviderID, err := m.GetDefaultKmsClusterID( + ctx, entity, defaultsToParent) + if err != nil { + return false, err + } + if defaultProviderID == "" { + return false, nil + } + return m.IsNativeProvider(ctx, defaultProviderID) +} + +func (m ManagerKmip) IsNativeProvider( + ctx context.Context, + providerID string) (bool, error) { + + info, err := m.GetClusterStatus(ctx, providerID) + if err != nil { + return false, err + } + if info == nil { + return false, nil + } + return info.ManagementType == string( + types.KmipClusterInfoKmsManagementTypeNativeProvider), nil +} + +func (m ManagerKmip) GetDefaultKmsClusterID( + ctx context.Context, + entity *types.ManagedObjectReference, + defaultsToParent bool) (string, error) { + + req := types.GetDefaultKmsCluster{ + This: m.Reference(), + Entity: entity, + DefaultsToParent: &defaultsToParent, + } + res, err := methods.GetDefaultKmsCluster(ctx, m.Client(), &req) + if err != nil { + return "", err + } + if res.Returnval != nil { + return res.Returnval.Id, nil + } + return "", nil +} + +func (m ManagerKmip) GetStatus( + ctx context.Context, + clusters ...types.KmipClusterInfo) ([]types.CryptoManagerKmipClusterStatus, error) { + + req := types.RetrieveKmipServersStatus_Task{ + This: m.Reference(), + Clusters: clusters, + } + res, err := methods.RetrieveKmipServersStatus_Task(ctx, m.Client(), &req) + if err != nil { + return nil, err + } + + task := object.NewTask(m.Client(), res.Returnval) + taskInfo, err := task.WaitForResult(ctx) + if err != nil { + return nil, err + } + + if taskInfo.Result == nil { + return nil, nil + } + result, ok := taskInfo.Result.(types.ArrayOfCryptoManagerKmipClusterStatus) + if !ok { + return nil, nil + } + if len(result.CryptoManagerKmipClusterStatus) == 0 { + return nil, nil + } + + return result.CryptoManagerKmipClusterStatus, nil +} + +func (m ManagerKmip) GetClusterStatus( + ctx context.Context, + providerID string) (*types.CryptoManagerKmipClusterStatus, error) { + + result, err := m.GetStatus( + ctx, + types.KmipClusterInfo{ + ClusterId: types.KeyProviderId{ + Id: providerID, + }, + }) + if err != nil { + return nil, err + } + if len(result) == 0 { + return nil, fmt.Errorf("invalid cluster ID") + } + return &result[0], nil +} + +func (m ManagerKmip) GetServerStatus( + ctx context.Context, + providerID, serverName string) (*types.CryptoManagerKmipServerStatus, error) { + + result, err := m.GetStatus( + ctx, + types.KmipClusterInfo{ + ClusterId: types.KeyProviderId{ + Id: providerID, + }, + }) + if err != nil { + return nil, err + } + if len(result) == 0 { + return nil, fmt.Errorf("invalid cluster ID") + } + if len(result[0].Servers) == 0 { + return nil, fmt.Errorf("invalid server name") + } + return &result[0].Servers[0], nil +} + +func (m ManagerKmip) MarkDefault( + ctx context.Context, + providerID string) error { + + req := types.MarkDefault{ + This: m.Reference(), + ClusterId: types.KeyProviderId{Id: providerID}, + } + _, err := methods.MarkDefault(ctx, m.Client(), &req) + if err != nil { + return err + } + return nil +} + +func (m ManagerKmip) SetDefaultKmsClusterId( + ctx context.Context, + providerID string, + entity *types.ManagedObjectReference) error { + + req := types.SetDefaultKmsCluster{ + This: m.Reference(), + Entity: entity, + } + if providerID != "" { + req.ClusterId = &types.KeyProviderId{ + Id: providerID, + } + } + _, err := methods.SetDefaultKmsCluster(ctx, m.Client(), &req) + if err != nil { + return err + } + return nil +} + +func (m ManagerKmip) RegisterKmipCluster( + ctx context.Context, + providerID string, + managementType types.KmipClusterInfoKmsManagementType) error { + + req := types.RegisterKmsCluster{ + This: m.Reference(), + ClusterId: types.KeyProviderId{ + Id: providerID, + }, + ManagementType: string(managementType), + } + _, err := methods.RegisterKmsCluster(ctx, m.Client(), &req) + if err != nil { + return err + } + return nil +} + +func (m ManagerKmip) UnregisterKmsCluster( + ctx context.Context, + providerID string) error { + + req := types.UnregisterKmsCluster{ + This: m.Reference(), + ClusterId: types.KeyProviderId{ + Id: providerID, + }, + } + _, err := methods.UnregisterKmsCluster(ctx, m.Client(), &req) + if err != nil { + return err + } + return nil +} + +func (m ManagerKmip) RegisterKmipServer( + ctx context.Context, + server types.KmipServerSpec) error { + + req := types.RegisterKmipServer{ + This: m.Reference(), + Server: server, + } + _, err := methods.RegisterKmipServer(ctx, m.Client(), &req) + if err != nil { + return err + } + return nil +} + +func (m ManagerKmip) UpdateKmipServer( + ctx context.Context, + server types.KmipServerSpec) error { + + req := types.UpdateKmipServer{ + This: m.Reference(), + Server: server, + } + _, err := methods.UpdateKmipServer(ctx, m.Client(), &req) + if err != nil { + return err + } + return nil +} + +func (m ManagerKmip) RemoveKmipServer( + ctx context.Context, + providerID, serverName string) error { + + req := types.RemoveKmipServer{ + This: m.Reference(), + ClusterId: types.KeyProviderId{ + Id: providerID, + }, + ServerName: serverName, + } + _, err := methods.RemoveKmipServer(ctx, m.Client(), &req) + if err != nil { + return err + } + return nil +} + +func (m ManagerKmip) ListKeys( + ctx context.Context, + limit *int32) ([]types.CryptoKeyId, error) { + + req := types.ListKeys{ + This: m.Reference(), + Limit: limit, + } + res, err := methods.ListKeys(ctx, m.Client(), &req) + if err != nil { + return nil, err + } + return res.Returnval, nil +} + +func (m ManagerKmip) IsValidKey( + ctx context.Context, + keyID string) (bool, error) { + + keys, err := m.ListKeys(ctx, nil) + if err != nil { + return false, err + } + + for i := range keys { + if keys[i].KeyId == keyID { + return true, nil + } + } + + return false, nil +} + +func (m ManagerKmip) IsValidProvider( + ctx context.Context, + providerID string) (bool, error) { + + clusters, err := m.ListKmipServers(ctx, nil) + if err != nil { + return false, err + } + + for i := range clusters { + if clusters[i].ClusterId.Id == providerID { + return true, nil + } + } + + return false, nil +} + +func (m ManagerKmip) IsValidServer( + ctx context.Context, + providerID, serverName string) (bool, error) { + + clusters, err := m.ListKmipServers(ctx, nil) + if err != nil { + return false, err + } + + for i := range clusters { + if clusters[i].ClusterId.Id == providerID { + for j := range clusters[i].Servers { + if clusters[i].Servers[j].Name == serverName { + return true, nil + } + } + } + } + + return false, nil +} + +func (m ManagerKmip) GenerateKey( + ctx context.Context, + providerID string) (string, error) { + + req := types.GenerateKey{ + This: m.Reference(), + } + + if providerID != "" { + req.KeyProvider = &types.KeyProviderId{ + Id: providerID, + } + } + res, err := methods.GenerateKey(ctx, m.Client(), &req) + if err != nil { + return "", err + } + if !res.Returnval.Success { + err := generateKeyError{reason: res.Returnval.Reason} + if res.Returnval.Fault != nil { + err.LocalizedMethodFault = *res.Returnval.Fault + } + return "", err + } + return res.Returnval.KeyId.KeyId, nil +} + +type generateKeyError struct { + types.LocalizedMethodFault + reason string +} + +func (e generateKeyError) Error() string { + + return e.reason +} + +func (e generateKeyError) GetLocalizedMethodFault() *types.LocalizedMethodFault { + return &e.LocalizedMethodFault +} diff --git a/crypto/manager_kmip_test.go b/crypto/manager_kmip_test.go new file mode 100644 index 000000000..ca044f6ba --- /dev/null +++ b/crypto/manager_kmip_test.go @@ -0,0 +1,998 @@ +/* +Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package crypto_test + +import ( + "context" + "math" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + + "github.com/vmware/govmomi/crypto" + "github.com/vmware/govmomi/fault" + "github.com/vmware/govmomi/object" + "github.com/vmware/govmomi/simulator" + "github.com/vmware/govmomi/vim25" + "github.com/vmware/govmomi/vim25/types" +) + +// CryptoManagerKmip should implement the Reference interface. +var _ object.Reference = crypto.ManagerKmip{} + +func TestCryptoManagerKmip(t *testing.T) { + + t.Run("RegisterKmipCluster", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + providerID := uuid.NewString() + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + + isValid, err := m.IsValidProvider(ctx, providerID) + assert.NoError(t, err) + assert.True(t, isValid) + + err = m.RegisterKmipCluster( + ctx, + providerID, + types.KmipClusterInfoKmsManagementTypeUnknown) + assert.EqualError(t, err, "ServerFaultCode: Already registered") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + }) + }) + + t.Run("GetClusterStatus", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + providerID := uuid.NewString() + + status, err := m.GetClusterStatus(ctx, providerID) + assert.EqualError(t, err, "invalid cluster ID") + assert.Nil(t, status) + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + + status, err = m.GetClusterStatus(ctx, providerID) + assert.NoError(t, err) + assert.NotNil(t, status) + assert.Equal(t, providerID, status.ClusterId.Id) + }) + }) + + t.Run("UnregisterKmsCluster", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + providerID := uuid.NewString() + + err = m.UnregisterKmsCluster(ctx, providerID) + assert.EqualError(t, err, "ServerFaultCode: Invalid cluster ID") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + + isValid, err := m.IsValidProvider(ctx, providerID) + assert.NoError(t, err) + assert.True(t, isValid) + + assert.NoError(t, m.UnregisterKmsCluster(ctx, providerID)) + + isValid, err = m.IsValidProvider(ctx, providerID) + assert.NoError(t, err) + assert.False(t, isValid) + }) + }) + + t.Run("IsValidProvider", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + providerID := uuid.NewString() + + ok, err := m.IsValidProvider(ctx, providerID) + assert.NoError(t, err) + assert.False(t, ok) + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + + ok, err = m.IsValidProvider(ctx, providerID) + assert.NoError(t, err) + assert.True(t, ok) + }) + }) + + t.Run("GetDefaultKmsClusterID", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + provider1ID := uuid.NewString() + provider2ID := uuid.NewString() + provider3ID := uuid.NewString() + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider1ID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider2ID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider3ID, + types.KmipClusterInfoKmsManagementTypeNativeProvider)) + + defaultProviderID, err := m.GetDefaultKmsClusterID(ctx, nil, true) + assert.EqualError(t, err, "ServerFaultCode: No default provider") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + assert.Empty(t, defaultProviderID) + + assert.NoError(t, m.MarkDefault(ctx, provider3ID)) + defaultProviderID, err = m.GetDefaultKmsClusterID(ctx, nil, true) + assert.NoError(t, err) + assert.Equal(t, provider3ID, defaultProviderID) + + // Assert setting the default a second time does not return an + // error. + assert.NoError(t, m.MarkDefault(ctx, provider3ID)) + defaultProviderID, err = m.GetDefaultKmsClusterID(ctx, nil, true) + assert.NoError(t, err) + assert.Equal(t, provider3ID, defaultProviderID) + + fakeMoRef := types.ManagedObjectReference{ + Type: "fake", + Value: "fake", + } + + defaultProviderID, err = m.GetDefaultKmsClusterID(ctx, &fakeMoRef, true) + assert.EqualError(t, err, "ServerFaultCode: No default provider") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + assert.Empty(t, defaultProviderID) + + assert.NoError(t, m.SetDefaultKmsClusterId( + ctx, provider2ID, &fakeMoRef)) + defaultProviderID, err = m.GetDefaultKmsClusterID(ctx, &fakeMoRef, true) + assert.NoError(t, err) + assert.Equal(t, provider2ID, defaultProviderID) + + // Assert setting the default for an entity a second time does not + // return an error. + assert.NoError(t, m.SetDefaultKmsClusterId( + ctx, provider2ID, &fakeMoRef)) + defaultProviderID, err = m.GetDefaultKmsClusterID(ctx, &fakeMoRef, true) + assert.NoError(t, err) + assert.Equal(t, provider2ID, defaultProviderID) + + // Remove the default for the entity. + assert.NoError(t, m.SetDefaultKmsClusterId(ctx, "", &fakeMoRef)) + defaultProviderID, err = m.GetDefaultKmsClusterID(ctx, &fakeMoRef, true) + assert.EqualError(t, err, "ServerFaultCode: No default provider") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + assert.Empty(t, defaultProviderID) + + // Remove the default. + assert.NoError(t, m.SetDefaultKmsClusterId(ctx, "", nil)) + assert.EqualError(t, err, "ServerFaultCode: No default provider") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + assert.Empty(t, defaultProviderID) + }) + }) + + t.Run("RegisterKmipServer", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + providerID := uuid.NewString() + serverName := uuid.NewString() + + serverSpec := types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: providerID, + }, + Info: types.KmipServerInfo{ + Name: serverName, + }, + } + + err = m.RegisterKmipServer(ctx, serverSpec) + assert.EqualError(t, err, "ServerFaultCode: Invalid cluster ID") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + + assert.NoError(t, m.RegisterKmipServer(ctx, serverSpec)) + + ok, err := m.IsValidServer(ctx, providerID, serverName) + assert.NoError(t, err) + assert.True(t, ok) + + err = m.RegisterKmipServer(ctx, serverSpec) + assert.EqualError(t, err, "ServerFaultCode: Already registered") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + }) + }) + + t.Run("GetServerStatus", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + providerID := uuid.NewString() + serverName := uuid.NewString() + + status, err := m.GetServerStatus(ctx, providerID, serverName) + assert.EqualError(t, err, "invalid cluster ID") + assert.Nil(t, status) + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + + status, err = m.GetServerStatus(ctx, providerID, serverName) + assert.EqualError(t, err, "invalid server name") + assert.Nil(t, status) + + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: providerID, + }, + Info: types.KmipServerInfo{ + Name: serverName, + }, + })) + + status, err = m.GetServerStatus(ctx, providerID, serverName) + assert.NoError(t, err) + assert.NotNil(t, status) + assert.Equal(t, serverName, status.Name) + }) + }) + + t.Run("ListKmipServers", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + clusters, err := m.ListKmipServers(ctx, nil) + assert.NoError(t, err) + assert.Len(t, clusters, 0) + + provider1ID := uuid.NewString() + provider2ID := uuid.NewString() + provider3ID := uuid.NewString() + + provider1serverName1 := uuid.NewString() + provider1serverName2 := uuid.NewString() + provider2serverName1 := uuid.NewString() + provider2serverName2 := uuid.NewString() + provider2serverName3 := uuid.NewString() + provider3serverName1 := uuid.NewString() + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider1ID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider2ID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider3ID, + types.KmipClusterInfoKmsManagementTypeNativeProvider)) + + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: provider1ID, + }, + Info: types.KmipServerInfo{ + Name: provider1serverName1, + }, + })) + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: provider1ID, + }, + Info: types.KmipServerInfo{ + Name: provider1serverName2, + }, + })) + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: provider2ID, + }, + Info: types.KmipServerInfo{ + Name: provider2serverName1, + }, + })) + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: provider2ID, + }, + Info: types.KmipServerInfo{ + Name: provider2serverName2, + }, + })) + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: provider2ID, + }, + Info: types.KmipServerInfo{ + Name: provider2serverName3, + }, + })) + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: provider3ID, + }, + Info: types.KmipServerInfo{ + Name: provider3serverName1, + }, + })) + + clusters, err = m.ListKmipServers(ctx, nil) + assert.NoError(t, err) + assert.Len(t, clusters, 3) + + assert.Equal(t, clusters[0].ClusterId.Id, provider1ID) + assert.Equal(t, clusters[0].ManagementType, string(types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.Len(t, clusters[0].Servers, 2) + assert.Equal(t, clusters[0].Servers[0].Name, provider1serverName1) + assert.Equal(t, clusters[0].Servers[1].Name, provider1serverName2) + + assert.Equal(t, clusters[1].ClusterId.Id, provider2ID) + assert.Equal(t, clusters[1].ManagementType, string(types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.Len(t, clusters[1].Servers, 3) + assert.Equal(t, clusters[1].Servers[0].Name, provider2serverName1) + assert.Equal(t, clusters[1].Servers[1].Name, provider2serverName2) + assert.Equal(t, clusters[1].Servers[2].Name, provider2serverName3) + + assert.Equal(t, clusters[2].ClusterId.Id, provider3ID) + assert.Equal(t, clusters[2].ManagementType, string(types.KmipClusterInfoKmsManagementTypeNativeProvider)) + assert.Len(t, clusters[2].Servers, 1) + assert.Equal(t, clusters[2].Servers[0].Name, provider3serverName1) + + // List all with a limit. + clusters, err = m.ListKmipServers(ctx, types.NewInt32(math.MaxInt32)) + assert.NoError(t, err) + assert.Len(t, clusters, 3) + + assert.Equal(t, clusters[0].ClusterId.Id, provider1ID) + assert.Equal(t, clusters[0].ManagementType, string(types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.Len(t, clusters[0].Servers, 2) + assert.Equal(t, clusters[0].Servers[0].Name, provider1serverName1) + assert.Equal(t, clusters[0].Servers[1].Name, provider1serverName2) + + assert.Equal(t, clusters[1].ClusterId.Id, provider2ID) + assert.Equal(t, clusters[1].ManagementType, string(types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.Len(t, clusters[1].Servers, 3) + assert.Equal(t, clusters[1].Servers[0].Name, provider2serverName1) + assert.Equal(t, clusters[1].Servers[1].Name, provider2serverName2) + assert.Equal(t, clusters[1].Servers[2].Name, provider2serverName3) + + assert.Equal(t, clusters[2].ClusterId.Id, provider3ID) + assert.Equal(t, clusters[2].ManagementType, string(types.KmipClusterInfoKmsManagementTypeNativeProvider)) + assert.Len(t, clusters[2].Servers, 1) + assert.Equal(t, clusters[2].Servers[0].Name, provider3serverName1) + + // List the first cluster. + clusters, err = m.ListKmipServers(ctx, types.NewInt32(1)) + assert.NoError(t, err) + assert.Len(t, clusters, 1) + + assert.Equal(t, clusters[0].ClusterId.Id, provider1ID) + assert.Equal(t, clusters[0].ManagementType, string(types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.Len(t, clusters[0].Servers, 2) + assert.Equal(t, clusters[0].Servers[0].Name, provider1serverName1) + assert.Equal(t, clusters[0].Servers[1].Name, provider1serverName2) + + // List the first and second cluster. + clusters, err = m.ListKmipServers(ctx, types.NewInt32(2)) + assert.NoError(t, err) + assert.Len(t, clusters, 2) + + assert.Equal(t, clusters[0].ClusterId.Id, provider1ID) + assert.Equal(t, clusters[0].ManagementType, string(types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.Len(t, clusters[0].Servers, 2) + assert.Equal(t, clusters[0].Servers[0].Name, provider1serverName1) + assert.Equal(t, clusters[0].Servers[1].Name, provider1serverName2) + + assert.Equal(t, clusters[1].ClusterId.Id, provider2ID) + assert.Equal(t, clusters[1].ManagementType, string(types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.Len(t, clusters[1].Servers, 3) + assert.Equal(t, clusters[1].Servers[0].Name, provider2serverName1) + assert.Equal(t, clusters[1].Servers[1].Name, provider2serverName2) + assert.Equal(t, clusters[1].Servers[2].Name, provider2serverName3) + }) + }) + + t.Run("UpdateKmipServer", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + providerID := uuid.NewString() + serverName := uuid.NewString() + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + + spec := types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: providerID, + }, + Info: types.KmipServerInfo{ + Name: serverName, + }, + } + + assert.NoError(t, m.RegisterKmipServer(ctx, spec)) + + ok, err := m.IsValidServer(ctx, providerID, serverName) + assert.NoError(t, err) + assert.True(t, ok) + + spec.ClusterId.Id = "invalid" + spec.Info.Name = "invalid" + spec.Info.Port = 123 + + err = m.UpdateKmipServer(ctx, spec) + assert.EqualError(t, err, "ServerFaultCode: Invalid cluster ID") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + + clusters, err := m.ListKmipServers(ctx, nil) + assert.NoError(t, err) + assert.Len(t, clusters, 1) + assert.Len(t, clusters[0].Servers, 1) + assert.Equal(t, int32(0), clusters[0].Servers[0].Port) + + spec.ClusterId.Id = providerID + + err = m.UpdateKmipServer(ctx, spec) + assert.EqualError(t, err, "ServerFaultCode: Invalid server name") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + + clusters, err = m.ListKmipServers(ctx, nil) + assert.NoError(t, err) + assert.Len(t, clusters, 1) + assert.Len(t, clusters[0].Servers, 1) + assert.Equal(t, int32(0), clusters[0].Servers[0].Port) + + spec.Info.Name = serverName + + assert.NoError(t, m.UpdateKmipServer(ctx, spec)) + + clusters, err = m.ListKmipServers(ctx, nil) + assert.NoError(t, err) + assert.Len(t, clusters, 1) + assert.Len(t, clusters[0].Servers, 1) + assert.Equal(t, int32(123), clusters[0].Servers[0].Port) + }) + }) + + t.Run("RemoveKmipServer", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + providerID := uuid.NewString() + serverName := uuid.NewString() + + err = m.RemoveKmipServer(ctx, providerID, serverName) + assert.EqualError(t, err, "ServerFaultCode: Invalid cluster ID") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + + err = m.RemoveKmipServer(ctx, providerID, serverName) + assert.EqualError(t, err, "ServerFaultCode: Invalid server name") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: providerID, + }, + Info: types.KmipServerInfo{ + Name: serverName, + }, + })) + + ok, err := m.IsValidServer(ctx, providerID, serverName) + assert.NoError(t, err) + assert.True(t, ok) + + assert.NoError(t, m.RemoveKmipServer(ctx, providerID, serverName)) + + ok, err = m.IsValidServer(ctx, providerID, serverName) + assert.NoError(t, err) + assert.False(t, ok) + }) + }) + + t.Run("IsValidServer", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + providerID := uuid.NewString() + serverName := uuid.NewString() + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: providerID, + }, + Info: types.KmipServerInfo{ + Name: serverName, + }, + })) + + ok, err := m.IsValidServer(ctx, providerID, serverName) + assert.NoError(t, err) + assert.True(t, ok) + + assert.NoError(t, m.RemoveKmipServer(ctx, providerID, serverName)) + + ok, err = m.IsValidServer(ctx, providerID, serverName) + assert.NoError(t, err) + assert.False(t, ok) + }) + }) + + t.Run("GetStatus", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + status, err := m.GetStatus(ctx) + assert.NoError(t, err) + assert.Nil(t, status) + + provider1ID := uuid.NewString() + provider2ID := uuid.NewString() + provider3ID := uuid.NewString() + + provider1serverName1 := uuid.NewString() + provider1serverName2 := uuid.NewString() + provider2serverName1 := uuid.NewString() + provider2serverName2 := uuid.NewString() + provider2serverName3 := uuid.NewString() + provider3serverName1 := uuid.NewString() + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider1ID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider2ID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider3ID, + types.KmipClusterInfoKmsManagementTypeNativeProvider)) + + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: provider1ID, + }, + Info: types.KmipServerInfo{ + Name: provider1serverName1, + }, + })) + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: provider1ID, + }, + Info: types.KmipServerInfo{ + Name: provider1serverName2, + }, + })) + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: provider2ID, + }, + Info: types.KmipServerInfo{ + Name: provider2serverName1, + }, + })) + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: provider2ID, + }, + Info: types.KmipServerInfo{ + Name: provider2serverName2, + }, + })) + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: provider2ID, + }, + Info: types.KmipServerInfo{ + Name: provider2serverName3, + }, + })) + assert.NoError(t, m.RegisterKmipServer(ctx, types.KmipServerSpec{ + ClusterId: types.KeyProviderId{ + Id: provider3ID, + }, + Info: types.KmipServerInfo{ + Name: provider3serverName1, + }, + })) + + status, err = m.GetStatus(ctx) + assert.NoError(t, err) + assert.NotNil(t, status) + assert.Len(t, status, 3) + + assert.Equal(t, status[0].ClusterId.Id, provider1ID) + assert.Equal(t, status[0].ManagementType, string(types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.Len(t, status[0].Servers, 2) + assert.Equal(t, status[0].Servers[0].Name, provider1serverName1) + assert.Equal(t, status[0].Servers[1].Name, provider1serverName2) + + assert.Equal(t, status[1].ClusterId.Id, provider2ID) + assert.Equal(t, status[1].ManagementType, string(types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.Len(t, status[1].Servers, 3) + assert.Equal(t, status[1].Servers[0].Name, provider2serverName1) + assert.Equal(t, status[1].Servers[1].Name, provider2serverName2) + assert.Equal(t, status[1].Servers[2].Name, provider2serverName3) + + assert.Equal(t, status[2].ClusterId.Id, provider3ID) + assert.Equal(t, status[2].ManagementType, string(types.KmipClusterInfoKmsManagementTypeNativeProvider)) + assert.Len(t, status[2].Servers, 1) + assert.Equal(t, status[2].Servers[0].Name, provider3serverName1) + + status, err = m.GetStatus(ctx, types.KmipClusterInfo{ + ClusterId: types.KeyProviderId{ + Id: provider2ID, + }, + Servers: []types.KmipServerInfo{ + { + Name: provider2serverName2, + }, + }, + }) + assert.NoError(t, err) + assert.NotNil(t, status) + assert.Len(t, status, 1) + + assert.Equal(t, status[0].ClusterId.Id, provider2ID) + assert.Equal(t, status[0].ManagementType, string(types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.Len(t, status[0].Servers, 1) + assert.Equal(t, status[0].Servers[0].Name, provider2serverName2) + }) + }) + + t.Run("IsDefaultProviderNative", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + provider1ID := uuid.NewString() + provider2ID := uuid.NewString() + provider3ID := uuid.NewString() + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider1ID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider2ID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider3ID, + types.KmipClusterInfoKmsManagementTypeNativeProvider)) + + ok, err := m.IsDefaultProviderNative(ctx, nil, false) + assert.EqualError(t, err, "ServerFaultCode: No default provider") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + assert.False(t, ok) + + assert.NoError(t, m.MarkDefault(ctx, provider3ID)) + + ok, err = m.IsDefaultProviderNative(ctx, nil, false) + assert.NoError(t, err) + assert.True(t, ok) + }) + }) + + t.Run("MarkDefault", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + provider1ID := uuid.NewString() + provider2ID := uuid.NewString() + provider3ID := uuid.NewString() + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider1ID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider2ID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider3ID, + types.KmipClusterInfoKmsManagementTypeNativeProvider)) + + assert.NoError(t, m.MarkDefault(ctx, provider2ID)) + defaultProviderID, err := m.GetDefaultKmsClusterID(ctx, nil, true) + assert.NoError(t, err) + assert.Equal(t, provider2ID, defaultProviderID) + + assert.NoError(t, m.MarkDefault(ctx, provider1ID)) + defaultProviderID, err = m.GetDefaultKmsClusterID(ctx, nil, true) + assert.NoError(t, err) + assert.Equal(t, provider1ID, defaultProviderID) + + assert.NoError(t, m.MarkDefault(ctx, provider3ID)) + defaultProviderID, err = m.GetDefaultKmsClusterID(ctx, nil, true) + assert.NoError(t, err) + assert.Equal(t, provider3ID, defaultProviderID) + + assert.NoError(t, m.MarkDefault(ctx, "")) + defaultProviderID, err = m.GetDefaultKmsClusterID(ctx, nil, true) + assert.EqualError(t, err, "ServerFaultCode: No default provider") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + assert.Empty(t, defaultProviderID) + }) + }) + + t.Run("SetDefaultKmsClusterId", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + provider1ID := uuid.NewString() + provider2ID := uuid.NewString() + provider3ID := uuid.NewString() + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider1ID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider2ID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + provider3ID, + types.KmipClusterInfoKmsManagementTypeNativeProvider)) + + assert.NoError(t, m.SetDefaultKmsClusterId(ctx, provider2ID, nil)) + defaultProviderID, err := m.GetDefaultKmsClusterID(ctx, nil, true) + assert.NoError(t, err) + assert.Equal(t, provider2ID, defaultProviderID) + + assert.NoError(t, m.SetDefaultKmsClusterId(ctx, provider1ID, nil)) + defaultProviderID, err = m.GetDefaultKmsClusterID(ctx, nil, true) + assert.NoError(t, err) + assert.Equal(t, provider1ID, defaultProviderID) + + assert.NoError(t, m.SetDefaultKmsClusterId(ctx, provider3ID, nil)) + defaultProviderID, err = m.GetDefaultKmsClusterID(ctx, nil, true) + assert.NoError(t, err) + assert.Equal(t, provider3ID, defaultProviderID) + + err = m.SetDefaultKmsClusterId(ctx, "invalid", nil) + assert.EqualError(t, err, "ServerFaultCode: Invalid cluster ID") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + + assert.NoError(t, m.SetDefaultKmsClusterId(ctx, "", nil)) + defaultProviderID, err = m.GetDefaultKmsClusterID(ctx, nil, true) + assert.EqualError(t, err, "ServerFaultCode: No default provider") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + assert.Empty(t, defaultProviderID) + }) + }) + + t.Run("GenerateKey", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + providerID1 := uuid.NewString() + providerID2 := uuid.NewString() + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID1, + types.KmipClusterInfoKmsManagementTypeNativeProvider)) + + keyID, err := m.GenerateKey(ctx, "") + assert.EqualError(t, err, "ServerFaultCode: No default provider") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + assert.Empty(t, keyID) + + assert.NoError(t, m.MarkDefault(ctx, providerID1)) + + keyID, err = m.GenerateKey(ctx, providerID1) + assert.EqualError(t, err, + "ServerFaultCode: Cannot generate keys with native key provider") + assert.True(t, fault.Is(err, &types.RuntimeFault{})) + assert.Empty(t, keyID) + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID2, + types.KmipClusterInfoKmsManagementTypeUnknown)) + + keyID, err = m.GenerateKey(ctx, providerID2) + assert.NoError(t, err) + assert.NotEmpty(t, keyID) + + assert.NoError(t, m.MarkDefault(ctx, providerID2)) + + keyID, err = m.GenerateKey(ctx, "") + assert.NoError(t, err) + assert.NotEmpty(t, keyID) + }) + }) + + t.Run("ListKeys", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + providerID1 := uuid.NewString() + providerID2 := uuid.NewString() + providerID3 := uuid.NewString() + + keys, err := m.ListKeys(ctx, nil) + assert.NoError(t, err) + assert.Len(t, keys, 0) + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID1, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID2, + types.KmipClusterInfoKmsManagementTypeUnknown)) + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID3, + types.KmipClusterInfoKmsManagementTypeNativeProvider)) + + keyID1, err := m.GenerateKey(ctx, providerID2) + assert.NoError(t, err) + assert.NotEmpty(t, keyID1) + + assert.NoError(t, m.MarkDefault(ctx, providerID2)) + keyID2, err := m.GenerateKey(ctx, "") + assert.NoError(t, err) + assert.NotEmpty(t, keyID2) + + assert.NoError(t, m.MarkDefault(ctx, providerID1)) + keyID3, err := m.GenerateKey(ctx, "") + assert.NoError(t, err) + assert.NotEmpty(t, keyID3) + + keys, err = m.ListKeys(ctx, nil) + assert.NoError(t, err) + assert.Len(t, keys, 3) + assert.ElementsMatch(t, keys, []types.CryptoKeyId{ + { + KeyId: keyID1, + ProviderId: &types.KeyProviderId{Id: providerID2}, + }, + { + KeyId: keyID2, + ProviderId: &types.KeyProviderId{Id: providerID2}, + }, + { + KeyId: keyID3, + ProviderId: &types.KeyProviderId{Id: providerID1}, + }, + }) + + keys, err = m.ListKeys(ctx, types.NewInt32(math.MaxInt32)) + assert.NoError(t, err) + assert.Len(t, keys, 3) + assert.ElementsMatch(t, keys, []types.CryptoKeyId{ + { + KeyId: keyID1, + ProviderId: &types.KeyProviderId{Id: providerID2}, + }, + { + KeyId: keyID2, + ProviderId: &types.KeyProviderId{Id: providerID2}, + }, + { + KeyId: keyID3, + ProviderId: &types.KeyProviderId{Id: providerID1}, + }, + }) + + keys, err = m.ListKeys(ctx, types.NewInt32(1)) + assert.NoError(t, err) + assert.Len(t, keys, 1) + + keys, err = m.ListKeys(ctx, types.NewInt32(2)) + assert.NoError(t, err) + assert.Len(t, keys, 2) + }) + }) + + t.Run("IsValidKey", func(t *testing.T) { + simulator.Test(func(ctx context.Context, c *vim25.Client) { + m, err := crypto.GetManagerKmip(c) + assert.NoError(t, err) + + providerID := uuid.NewString() + + assert.NoError(t, m.RegisterKmipCluster( + ctx, + providerID, + types.KmipClusterInfoKmsManagementTypeUnknown)) + + assert.NoError(t, m.MarkDefault(ctx, providerID)) + + keyID, err := m.GenerateKey(ctx, "") + assert.NoError(t, err) + assert.NotEmpty(t, keyID) + + ok, err := m.IsValidKey(ctx, keyID) + assert.NoError(t, err) + assert.True(t, ok) + }) + }) +} diff --git a/simulator/crypto_manager_kmip.go b/simulator/crypto_manager_kmip.go new file mode 100644 index 000000000..c52744d2a --- /dev/null +++ b/simulator/crypto_manager_kmip.go @@ -0,0 +1,515 @@ +/* +Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package simulator + +import ( + "slices" + + "github.com/google/uuid" + + "github.com/vmware/govmomi/vim25/methods" + "github.com/vmware/govmomi/vim25/mo" + "github.com/vmware/govmomi/vim25/soap" + "github.com/vmware/govmomi/vim25/types" +) + +const ( + nativeKeyProvider = string(types.KmipClusterInfoKmsManagementTypeNativeProvider) +) + +type CryptoManagerKmip struct { + mo.CryptoManagerKmip + + keyIDToProviderID map[string]string +} + +func (m *CryptoManagerKmip) init(r *Registry) { + if m.keyIDToProviderID == nil { + m.keyIDToProviderID = map[string]string{} + } +} + +func (m CryptoManagerKmip) moRefEq(a, b types.ManagedObjectReference) bool { + return a.Type == b.Type && a.Value == b.Value +} + +func (m *CryptoManagerKmip) ListKmipServers( + ctx *Context, req *types.ListKmipServers) soap.HasFault { + + body := methods.ListKmipServersBody{ + Res: &types.ListKmipServersResponse{}, + } + + if len(m.KmipServers) > 0 { + limit := len(m.KmipServers) + if req.Limit != nil { + if reqLimit := int(*req.Limit); reqLimit >= 0 && reqLimit < limit { + limit = reqLimit + } + } + body.Res.Returnval = m.KmipServers[0:limit] + } + + return &body + +} + +// TODO: Implement req.DefaultsToParent +func (m *CryptoManagerKmip) GetDefaultKmsCluster( + ctx *Context, req *types.GetDefaultKmsCluster) soap.HasFault { + + var ( + providerID string + body methods.GetDefaultKmsClusterBody + ) + + for i := range m.KmipServers { + c := m.KmipServers[i] + if req.Entity != nil { + for i := range c.UseAsEntityDefault { + if m.moRefEq(*req.Entity, c.UseAsEntityDefault[i]) { + providerID = c.ClusterId.Id + } + } + } else if c.UseAsDefault { + providerID = c.ClusterId.Id + } + if providerID != "" { + break + } + } + + if providerID == "" { + body.Fault_ = Fault("No default provider", &types.RuntimeFault{}) + } else { + body.Res = &types.GetDefaultKmsClusterResponse{ + Returnval: &types.KeyProviderId{Id: providerID}, + } + } + + return &body +} + +type retrieveKmipServerStatusTask struct { + *CryptoManagerKmip + get []types.KmipClusterInfo + ctx *Context +} + +func (c *retrieveKmipServerStatusTask) Run( + task *Task) (types.AnyType, types.BaseMethodFault) { + + var result []types.CryptoManagerKmipClusterStatus + + if len(c.get) == 0 { + c.get = make([]types.KmipClusterInfo, len(c.KmipServers)) + copy(c.get, c.KmipServers) + } + + for i := range c.get { + g := &c.get[i] + if len(g.Servers) == 0 { + for j := range c.KmipServers { + if g.ClusterId.Id == c.KmipServers[j].ClusterId.Id { + g.Servers = make( + []types.KmipServerInfo, len(c.KmipServers[j].Servers)) + copy(g.Servers, c.KmipServers[j].Servers) + } + } + } + } + + for i := range c.KmipServers { + for j := range c.get { + if c.KmipServers[i].ClusterId.Id == c.get[j].ClusterId.Id { + clusterStatus := types.CryptoManagerKmipClusterStatus{ + ClusterId: types.KeyProviderId{ + Id: c.KmipServers[i].ClusterId.Id, + }, + ManagementType: c.KmipServers[i].ManagementType, + OverallStatus: types.ManagedEntityStatusGreen, + } + for k := range c.KmipServers[i].Servers { + for l := range c.get[j].Servers { + if c.KmipServers[i].Servers[k].Name == c.get[j].Servers[l].Name { + clusterStatus.Servers = append( + clusterStatus.Servers, + types.CryptoManagerKmipServerStatus{ + Name: c.KmipServers[i].Servers[k].Name, + Status: types.ManagedEntityStatusGreen, + }, + ) + } + } + } + result = append(result, clusterStatus) + } + } + } + + return types.ArrayOfCryptoManagerKmipClusterStatus{ + CryptoManagerKmipClusterStatus: result, + }, nil +} + +func (m *CryptoManagerKmip) RetrieveKmipServersStatusTask( + ctx *Context, req *types.RetrieveKmipServersStatus_Task) soap.HasFault { + + var body methods.RetrieveKmipServersStatus_TaskBody + + runner := &retrieveKmipServerStatusTask{ + CryptoManagerKmip: m, + ctx: ctx, + get: req.Clusters, + } + task := CreateTask( + runner.Reference(), + "retrieveKmipServerStatus", + runner.Run) + + body.Res = &types.RetrieveKmipServersStatus_TaskResponse{ + Returnval: task.Run(ctx), + } + + return &body +} + +func (m *CryptoManagerKmip) MarkDefault( + ctx *Context, req *types.MarkDefault) soap.HasFault { + + return m.SetDefaultKmsCluster( + ctx, + &types.SetDefaultKmsCluster{ + This: req.This, + ClusterId: &types.KeyProviderId{ + Id: req.ClusterId.Id, + }, + }) + +} + +func (m *CryptoManagerKmip) SetDefaultKmsCluster( + ctx *Context, req *types.SetDefaultKmsCluster) soap.HasFault { + + var ( + validClusterID bool + body methods.SetDefaultKmsClusterBody + ) + + for i := range m.KmipServers { + c := &m.KmipServers[i] + if req.ClusterId != nil && req.ClusterId.Id != "" { + if c.ClusterId.Id != req.ClusterId.Id { + c.UseAsDefault = false + c.UseAsEntityDefault = nil + } else { + validClusterID = true + if req.Entity == nil { + c.UseAsDefault = true + } else { + found := false + for j := range c.UseAsEntityDefault { + if m.moRefEq(*req.Entity, c.UseAsEntityDefault[j]) { + found = true + break + } + } + if !found { + c.UseAsEntityDefault = append( + c.UseAsEntityDefault, + *req.Entity) + } + } + } + } else if req.Entity != nil { + x := -1 + for j := range c.UseAsEntityDefault { + if m.moRefEq(*req.Entity, c.UseAsEntityDefault[j]) { + x = j + break + } + } + if x >= 0 { + c.UseAsEntityDefault = slices.Delete( + c.UseAsEntityDefault, x, x+1) + } + } else { + c.UseAsDefault = false + } + } + + if req.ClusterId != nil && req.ClusterId.Id != "" && !validClusterID { + body.Fault_ = Fault("Invalid cluster ID", &types.RuntimeFault{}) + } else { + body.Res = &types.SetDefaultKmsClusterResponse{} + } + + return &body +} + +func (m *CryptoManagerKmip) RegisterKmsCluster( + ctx *Context, req *types.RegisterKmsCluster) soap.HasFault { + + var body methods.RegisterKmsClusterBody + + for i := range m.KmipServers { + if req.ClusterId.Id == m.KmipServers[i].ClusterId.Id { + body.Fault_ = Fault("Already registered", &types.RuntimeFault{}) + } + } + if body.Fault_ == nil { + body.Res = &types.RegisterKmsClusterResponse{} + m.KmipServers = append(m.KmipServers, + types.KmipClusterInfo{ + ClusterId: types.KeyProviderId{ + Id: req.ClusterId.Id, + }, + ManagementType: req.ManagementType, + }) + } + + return &body +} + +func (m *CryptoManagerKmip) UnregisterKmsCluster( + ctx *Context, req *types.UnregisterKmsCluster) soap.HasFault { + + var body methods.UnregisterKmsClusterBody + + x := -1 + for i := range m.KmipServers { + if req.ClusterId.Id == m.KmipServers[i].ClusterId.Id { + x = i + } + } + + if x < 0 { + body.Fault_ = Fault("Invalid cluster ID", &types.RuntimeFault{}) + } else { + m.KmipServers = slices.Delete(m.KmipServers, x, x+1) + body.Res = &types.UnregisterKmsClusterResponse{} + } + + return &body +} + +func (m *CryptoManagerKmip) RegisterKmipServer( + ctx *Context, req *types.RegisterKmipServer) soap.HasFault { + + var ( + validClusterID bool + alreadyRegistered bool + body methods.RegisterKmipServerBody + ) + + for i := range m.KmipServers { + c := &m.KmipServers[i] + + if req.Server.ClusterId.Id == c.ClusterId.Id { + validClusterID = true + for j := range c.Servers { + if req.Server.Info.Name == c.Servers[j].Name { + alreadyRegistered = true + break + } + } + if !alreadyRegistered { + c.Servers = append(c.Servers, req.Server.Info) + } + } + + if validClusterID || alreadyRegistered { + break + } + } + + if !validClusterID { + body.Fault_ = Fault("Invalid cluster ID", &types.RuntimeFault{}) + } else if alreadyRegistered { + body.Fault_ = Fault("Already registered", &types.RuntimeFault{}) + } else { + body.Res = &types.RegisterKmipServerResponse{} + } + + return &body +} + +func (m *CryptoManagerKmip) RemoveKmipServer( + ctx *Context, req *types.RemoveKmipServer) soap.HasFault { + + var ( + validClusterID bool + validServerName bool + body methods.RemoveKmipServerBody + ) + + for i := range m.KmipServers { + c := &m.KmipServers[i] + + if req.ClusterId.Id == c.ClusterId.Id { + validClusterID = true + + x := -1 + for j := range c.Servers { + if req.ServerName == c.Servers[j].Name { + x = j + break + } + } + + if x >= 0 { + validServerName = true + c.Servers = slices.Delete(c.Servers, x, x+1) + } + } + + if validClusterID { + break + } + } + + if !validClusterID { + body.Fault_ = Fault("Invalid cluster ID", &types.RuntimeFault{}) + } else if !validServerName { + body.Fault_ = Fault("Invalid server name", &types.RuntimeFault{}) + } else { + body.Res = &types.RemoveKmipServerResponse{} + } + + return &body +} + +func (m *CryptoManagerKmip) UpdateKmipServer( + ctx *Context, req *types.UpdateKmipServer) soap.HasFault { + + var ( + validClusterID bool + validServerName bool + body methods.UpdateKmipServerBody + ) + + for i := range m.KmipServers { + c := &m.KmipServers[i] + + if req.Server.ClusterId.Id == c.ClusterId.Id { + validClusterID = true + for j := range c.Servers { + if req.Server.Info.Name == c.Servers[j].Name { + validServerName = true + c.Servers[j] = req.Server.Info + break + } + } + } + + if validClusterID { + break + } + } + + if !validClusterID { + body.Fault_ = Fault("Invalid cluster ID", &types.RuntimeFault{}) + } else if !validServerName { + body.Fault_ = Fault("Invalid server name", &types.RuntimeFault{}) + } else { + body.Res = &types.UpdateKmipServerResponse{} + } + + return &body +} + +func (m *CryptoManagerKmip) GenerateKey( + ctx *Context, req *types.GenerateKey) soap.HasFault { + + var ( + provider types.KmipClusterInfo + body methods.GenerateKeyBody + ) + + for i := range m.KmipServers { + c := m.KmipServers[i] + if req.KeyProvider == nil { + if c.UseAsDefault { + provider = c + } + } else if req.KeyProvider.Id == c.ClusterId.Id { + provider = c + } + if provider.ClusterId.Id != "" { + break + } + } + + if provider.ClusterId.Id == "" { + body.Fault_ = Fault("No default provider", &types.RuntimeFault{}) + } else if provider.ManagementType == nativeKeyProvider { + body.Fault_ = Fault( + "Cannot generate keys with native key provider", + &types.RuntimeFault{}) + } else { + newKey := uuid.NewString() + m.keyIDToProviderID[newKey] = provider.ClusterId.Id + + body.Res = &types.GenerateKeyResponse{ + Returnval: types.CryptoKeyResult{ + Success: true, + KeyId: types.CryptoKeyId{ + KeyId: newKey, + ProviderId: &types.KeyProviderId{ + Id: provider.ClusterId.Id, + }, + }, + }, + } + } + + return &body +} + +func (m *CryptoManagerKmip) ListKeys( + ctx *Context, req *types.ListKeys) soap.HasFault { + + body := methods.ListKeysBody{ + Res: &types.ListKeysResponse{}, + } + + if len(m.keyIDToProviderID) > 0 { + var ( + i int + limit = len(m.keyIDToProviderID) + ) + if req.Limit != nil { + if reqLimit := int(*req.Limit); reqLimit >= 0 && reqLimit < limit { + limit = reqLimit + } + } + for keyID, providerID := range m.keyIDToProviderID { + if i >= limit { + break + } + i++ + body.Res.Returnval = append(body.Res.Returnval, types.CryptoKeyId{ + KeyId: keyID, + ProviderId: &types.KeyProviderId{ + Id: providerID, + }, + }) + } + } + + return &body +} diff --git a/simulator/model.go b/simulator/model.go index a595f1897..f16efc2e5 100644 --- a/simulator/model.go +++ b/simulator/model.go @@ -237,6 +237,7 @@ var kinds = map[string]reflect.Type{ "ClusterComputeResource": reflect.TypeOf((*ClusterComputeResource)(nil)).Elem(), "CustomFieldsManager": reflect.TypeOf((*CustomFieldsManager)(nil)).Elem(), "CustomizationSpecManager": reflect.TypeOf((*CustomizationSpecManager)(nil)).Elem(), + "CryptoManagerKmip": reflect.TypeOf((*CryptoManagerKmip)(nil)).Elem(), "Datacenter": reflect.TypeOf((*Datacenter)(nil)).Elem(), "Datastore": reflect.TypeOf((*Datastore)(nil)).Elem(), "DatastoreNamespaceManager": reflect.TypeOf((*DatastoreNamespaceManager)(nil)).Elem(), diff --git a/simulator/registry.go b/simulator/registry.go index eebfec907..70146e72f 100644 --- a/simulator/registry.go +++ b/simulator/registry.go @@ -535,6 +535,11 @@ func (r *Registry) FileManager() *FileManager { return r.Get(r.content().FileManager.Reference()).(*FileManager) } +// CryptoManager returns the CryptoManagerKmip singleton +func (r *Registry) CryptoManager() *CryptoManagerKmip { + return r.Get(r.content().CryptoManager.Reference()).(*CryptoManagerKmip) +} + type VirtualDiskManagerInterface interface { mo.Reference MO() mo.VirtualDiskManager