diff --git a/pkg/platformhubaccounts/platform_hub_account_resource.go b/pkg/platformhubaccounts/platform_hub_account_resource.go index ac91121e..2bdda439 100644 --- a/pkg/platformhubaccounts/platform_hub_account_resource.go +++ b/pkg/platformhubaccounts/platform_hub_account_resource.go @@ -8,12 +8,19 @@ import ( // PlatformHubAccountResource represents details for all Platform Hub accounts. type PlatformHubAccountResource struct { - AccountType PlatformHubAccountType `json:"AccountType" validate:"required,oneof=AmazonWebServicesAccount"` + AccountType PlatformHubAccountType `json:"AccountType" validate:"required,oneof=AmazonWebServicesAccount AmazonWebServicesOidcAccount"` Description string `json:"Description,omitempty"` Name string `json:"Name" validate:"required,notall"` AccessKey string `json:"AccessKey,omitempty"` SecretKey *core.SensitiveValue `json:"SecretKey,omitempty"` + // OIDC-specific fields + RoleArn string `json:"RoleArn,omitempty"` + SessionDuration string `json:"SessionDuration,omitempty"` + DeploymentSubjectKeys []string `json:"DeploymentSubjectKeys,omitempty"` + HealthCheckSubjectKeys []string `json:"HealthCheckSubjectKeys,omitempty"` + AccountTestSubjectKeys []string `json:"AccountTestSubjectKeys,omitempty"` + resources.Resource } @@ -70,6 +77,16 @@ func (r *PlatformHubAccountResource) ToPlatformHubAccount() (IPlatformHubAccount return nil, err } account = awsAccount + case AccountTypePlatformHubAwsOIDCAccount: + oidcAccount, err := NewPlatformHubAwsOIDCAccount(r.GetName(), r.RoleArn) + if err != nil { + return nil, err + } + oidcAccount.SessionDuration = r.SessionDuration + oidcAccount.DeploymentSubjectKeys = r.DeploymentSubjectKeys + oidcAccount.HealthCheckSubjectKeys = r.HealthCheckSubjectKeys + oidcAccount.AccountTestSubjectKeys = r.AccountTestSubjectKeys + account = oidcAccount default: return nil, internal.CreateInvalidParameterError("ToPlatformHubAccount", "AccountType") } @@ -101,6 +118,13 @@ func ToPlatformHubAccountResource(account IPlatformHubAccount) (*PlatformHubAcco awsAccount := account.(*PlatformHubAwsAccount) resource.AccessKey = awsAccount.AccessKey resource.SecretKey = awsAccount.SecretKey + case AccountTypePlatformHubAwsOIDCAccount: + oidcAccount := account.(*PlatformHubAwsOIDCAccount) + resource.RoleArn = oidcAccount.RoleArn + resource.SessionDuration = oidcAccount.SessionDuration + resource.DeploymentSubjectKeys = oidcAccount.DeploymentSubjectKeys + resource.HealthCheckSubjectKeys = oidcAccount.HealthCheckSubjectKeys + resource.AccountTestSubjectKeys = oidcAccount.AccountTestSubjectKeys default: return nil, internal.CreateInvalidParameterError("ToPlatformHubAccountResource", "AccountType") } diff --git a/pkg/platformhubaccounts/platform_hub_account_types.go b/pkg/platformhubaccounts/platform_hub_account_types.go index 7481e58c..34921516 100644 --- a/pkg/platformhubaccounts/platform_hub_account_types.go +++ b/pkg/platformhubaccounts/platform_hub_account_types.go @@ -3,5 +3,6 @@ package platformhubaccounts type PlatformHubAccountType string const ( - AccountTypePlatformHubAwsAccount = PlatformHubAccountType("AmazonWebServicesAccount") + AccountTypePlatformHubAwsAccount = PlatformHubAccountType("AmazonWebServicesAccount") + AccountTypePlatformHubAwsOIDCAccount = PlatformHubAccountType("AmazonWebServicesOidcAccount") ) diff --git a/pkg/platformhubaccounts/platform_hub_aws_oidc_account.go b/pkg/platformhubaccounts/platform_hub_aws_oidc_account.go new file mode 100644 index 00000000..77f7d42d --- /dev/null +++ b/pkg/platformhubaccounts/platform_hub_aws_oidc_account.go @@ -0,0 +1,58 @@ +package platformhubaccounts + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/v2/internal" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/constants" + validation "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/validation" + "github.com/go-playground/validator/v10" + "github.com/go-playground/validator/v10/non-standard/validators" +) + +// PlatformHubAwsOIDCAccount represents a Platform Hub AWS OIDC account. +type PlatformHubAwsOIDCAccount struct { + RoleArn string `json:"RoleArn" validate:"required"` + SessionDuration string `json:"SessionDuration,omitempty"` + DeploymentSubjectKeys []string `json:"DeploymentSubjectKeys,omitempty" validate:"omitempty,dive,oneof=space environment project tenant runbook account type'"` + HealthCheckSubjectKeys []string `json:"HealthCheckSubjectKeys,omitempty" validate:"omitempty,dive,oneof=space account target type'"` + AccountTestSubjectKeys []string `json:"AccountTestSubjectKeys,omitempty" validate:"omitempty,dive,oneof=space account type'"` + + platformHubAccount +} + +// NewPlatformHubAwsOIDCAccount initializes and returns a Platform Hub AWS OIDC account with a name and role ARN. +func NewPlatformHubAwsOIDCAccount(name string, roleArn string) (*PlatformHubAwsOIDCAccount, error) { + if internal.IsEmpty(name) { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError(constants.ParameterName) + } + + if internal.IsEmpty(roleArn) { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError("roleArn") + } + + account := PlatformHubAwsOIDCAccount{ + RoleArn: roleArn, + platformHubAccount: *newPlatformHubAccount(name, AccountTypePlatformHubAwsOIDCAccount), + } + + // validate to ensure that all expectations are met + err := account.Validate() + if err != nil { + return nil, err + } + + return &account, nil +} + +// Validate checks the state of this account and returns an error if invalid. +func (a *PlatformHubAwsOIDCAccount) Validate() error { + v := validator.New() + err := v.RegisterValidation("notblank", validators.NotBlank) + if err != nil { + return err + } + err = v.RegisterValidation("notall", validation.NotAll) + if err != nil { + return err + } + return v.Struct(a) +} diff --git a/pkg/platformhubaccounts/platform_hub_aws_oidc_account_test.go b/pkg/platformhubaccounts/platform_hub_aws_oidc_account_test.go new file mode 100644 index 00000000..eb42a413 --- /dev/null +++ b/pkg/platformhubaccounts/platform_hub_aws_oidc_account_test.go @@ -0,0 +1,205 @@ +package platformhubaccounts + +import ( + "encoding/json" + "fmt" + "testing" + "time" + + "github.com/OctopusDeploy/go-octopusdeploy/v2/internal" + "github.com/kinbiko/jsonassert" + "github.com/stretchr/testify/require" +) + +func TestPlatformHubAwsOIDCAccount(t *testing.T) { + name := internal.GetRandomName() + roleArn := "arn:aws:iam::123456789012:role/MyRole" + sessionDuration := "3600" + deploymentSubjectKeys := []string{"space", "environment", "project"} + healthCheckSubjectKeys := []string{"space", "target"} + accountTestSubjectKeys := []string{"space", "account"} + invalidDeploymentSubjectKeys := []string{"space", "invalid"} + invalidHealthCheckSubjectKeys := []string{"space", "project"} + invalidAccountTestSubjectKeys := []string{"space", "environment"} + + testCases := []struct { + TestName string + IsError bool + Name string + RoleArn string + SessionDuration string + DeploymentSubjectKeys []string + HealthCheckSubjectKeys []string + AccountTestSubjectKeys []string + }{ + {"Valid", false, name, roleArn, sessionDuration, deploymentSubjectKeys, healthCheckSubjectKeys, accountTestSubjectKeys}, + {"EmptyName", true, "", roleArn, sessionDuration, deploymentSubjectKeys, healthCheckSubjectKeys, accountTestSubjectKeys}, + {"WhitespaceName", true, " ", roleArn, sessionDuration, deploymentSubjectKeys, healthCheckSubjectKeys, accountTestSubjectKeys}, + {"EmptyRoleArn", true, name, "", sessionDuration, deploymentSubjectKeys, healthCheckSubjectKeys, accountTestSubjectKeys}, + {"NilSubjectKeys", false, name, roleArn, sessionDuration, nil, nil, nil}, + {"EmptySessionDuration", false, name, roleArn, "", deploymentSubjectKeys, healthCheckSubjectKeys, accountTestSubjectKeys}, + {"InvalidDeploymentSubjectKeys", true, name, roleArn, sessionDuration, invalidDeploymentSubjectKeys, healthCheckSubjectKeys, accountTestSubjectKeys}, + {"InvalidHealthCheckSubjectKeys", true, name, roleArn, sessionDuration, deploymentSubjectKeys, invalidHealthCheckSubjectKeys, accountTestSubjectKeys}, + {"InvalidAccountTestSubjectKeys", true, name, roleArn, sessionDuration, deploymentSubjectKeys, healthCheckSubjectKeys, invalidAccountTestSubjectKeys}, + } + for _, tc := range testCases { + t.Run(tc.TestName, func(t *testing.T) { + account := &PlatformHubAwsOIDCAccount{ + RoleArn: tc.RoleArn, + SessionDuration: tc.SessionDuration, + DeploymentSubjectKeys: tc.DeploymentSubjectKeys, + HealthCheckSubjectKeys: tc.HealthCheckSubjectKeys, + AccountTestSubjectKeys: tc.AccountTestSubjectKeys, + } + account.AccountType = AccountTypePlatformHubAwsOIDCAccount + account.Name = tc.Name + + if tc.IsError { + require.Error(t, account.Validate()) + } else { + require.NoError(t, account.Validate()) + require.Equal(t, AccountTypePlatformHubAwsOIDCAccount, account.GetAccountType()) + require.Equal(t, tc.Name, account.GetName()) + } + + account.SetName(tc.Name) + if tc.IsError { + require.Error(t, account.Validate()) + } else { + require.NoError(t, account.Validate()) + require.Equal(t, tc.Name, account.GetName()) + } + }) + } +} + +func TestPlatformHubAwsOIDCAccountNew(t *testing.T) { + name := internal.GetRandomName() + roleArn := "arn:aws:iam::123456789012:role/MyRole" + accountType := AccountTypePlatformHubAwsOIDCAccount + description := "" + + account, err := NewPlatformHubAwsOIDCAccount(name, roleArn) + + require.NotNil(t, account) + require.NoError(t, err) + require.NoError(t, account.Validate()) + + // resource + require.Equal(t, "", account.ID) + require.Equal(t, "", account.ModifiedBy) + require.Nil(t, account.ModifiedOn) + require.NotNil(t, account.Links) + + // IResource + require.Equal(t, "", account.GetID()) + require.Equal(t, "", account.GetModifiedBy()) + require.Nil(t, account.GetModifiedOn()) + require.NotNil(t, account.GetLinks()) + + // IPlatformHubAccount + require.Equal(t, accountType, account.GetAccountType()) + require.Equal(t, description, account.GetDescription()) + require.Equal(t, name, account.GetName()) + + // PlatformHubAwsOIDCAccount + require.Equal(t, roleArn, account.RoleArn) +} + +func TestPlatformHubAwsOIDCAccountMarshalJSON(t *testing.T) { + name := internal.GetRandomName() + roleArn := "arn:aws:iam::123456789012:role/MyRole" + sessionDuration := "3600" + deploymentSubjectKeys := []string{"space", "environment"} + healthCheckSubjectKeys := []string{"space", "target"} + accountTestSubjectKeys := []string{"space", "account"} + + deploymentSubjectKeysJSON, err := json.Marshal(deploymentSubjectKeys) + require.NoError(t, err) + require.NotNil(t, deploymentSubjectKeysJSON) + + healthCheckSubjectKeysJSON, err := json.Marshal(healthCheckSubjectKeys) + require.NoError(t, err) + require.NotNil(t, healthCheckSubjectKeysJSON) + + accountTestSubjectKeysJSON, err := json.Marshal(accountTestSubjectKeys) + require.NoError(t, err) + require.NotNil(t, accountTestSubjectKeysJSON) + + expectedJson := fmt.Sprintf(`{ + "AccountType": "AmazonWebServicesOidcAccount", + "Name": "%s", + "RoleArn": "%s", + "SessionDuration": "%s", + "DeploymentSubjectKeys": %s, + "HealthCheckSubjectKeys": %s, + "AccountTestSubjectKeys": %s + }`, name, roleArn, sessionDuration, deploymentSubjectKeysJSON, healthCheckSubjectKeysJSON, accountTestSubjectKeysJSON) + + account, err := NewPlatformHubAwsOIDCAccount(name, roleArn) + require.NoError(t, err) + require.NotNil(t, account) + + account.SessionDuration = sessionDuration + account.DeploymentSubjectKeys = deploymentSubjectKeys + account.HealthCheckSubjectKeys = healthCheckSubjectKeys + account.AccountTestSubjectKeys = accountTestSubjectKeys + + accountAsJSON, err := json.Marshal(account) + require.NoError(t, err) + require.NotNil(t, accountAsJSON) + + jsonassert.New(t).Assertf(expectedJson, string(accountAsJSON)) +} + +func TestPlatformHubAwsOIDCAccountNewWithConfigs(t *testing.T) { + name := internal.GetRandomName() + roleArn := "arn:aws:iam::123456789012:role/MyRole" + sessionDuration := "3600" + deploymentSubjectKeys := []string{"space", "environment", "project"} + healthCheckSubjectKeys := []string{"space", "target"} + accountTestSubjectKeys := []string{"space", "account"} + accountType := AccountTypePlatformHubAwsOIDCAccount + id := internal.GetRandomName() + modifiedBy := internal.GetRandomName() + modifiedOn := time.Now() + description := "Description for " + name + " (OK to Delete)" + + account, err := NewPlatformHubAwsOIDCAccount(name, roleArn) + require.NoError(t, err) + require.NotNil(t, account) + require.NoError(t, account.Validate()) + + account.Description = description + account.SessionDuration = sessionDuration + account.DeploymentSubjectKeys = deploymentSubjectKeys + account.HealthCheckSubjectKeys = healthCheckSubjectKeys + account.AccountTestSubjectKeys = accountTestSubjectKeys + account.ID = id + account.ModifiedBy = modifiedBy + account.ModifiedOn = &modifiedOn + + // resource + require.Equal(t, id, account.ID) + require.Equal(t, modifiedBy, account.ModifiedBy) + require.Equal(t, &modifiedOn, account.ModifiedOn) + require.NotNil(t, account.Links) + + // IResource + require.Equal(t, id, account.GetID()) + require.Equal(t, modifiedBy, account.GetModifiedBy()) + require.Equal(t, &modifiedOn, account.GetModifiedOn()) + require.NotNil(t, account.GetLinks()) + + // IPlatformHubAccount + require.Equal(t, accountType, account.GetAccountType()) + require.Equal(t, description, account.GetDescription()) + require.Equal(t, name, account.GetName()) + + // PlatformHubAwsOIDCAccount + require.Equal(t, roleArn, account.RoleArn) + require.Equal(t, sessionDuration, account.SessionDuration) + require.Equal(t, deploymentSubjectKeys, account.DeploymentSubjectKeys) + require.Equal(t, healthCheckSubjectKeys, account.HealthCheckSubjectKeys) + require.Equal(t, accountTestSubjectKeys, account.AccountTestSubjectKeys) +}