From 386a9dc382bcff23709d6930b51d6331a410228e Mon Sep 17 00:00:00 2001 From: Wilson de Carvalho <796900+wcmjunior@users.noreply.github.com> Date: Mon, 8 Apr 2024 18:32:36 -0700 Subject: [PATCH] Refactor remaining resources and data sources (#522) * Refactor cyral_integration_* * Remove dead code * Refactor cyral_policy * Refactor cyral_user_account * Refactor cyral_policy_rule and fix error in cyral_service_account * Refactor cyral_role and cyral_rego_policy_instance * Deprecate cyral_saml_configuration and improve deprecation messages * Refactor cyral_sidecar_instance, cyral_sidecar_instance_stats and cyral_system_info * Improve package names;refactor cyral_sidecar_health, cyral_permission * Fix error handlers to avoid states getting out-of-sync * Update cyral/internal/regopolicy/schema_loader.go Co-authored-by: Victor Moraes --------- Co-authored-by: Victor Moraes --- cyral/core/README.md | 4 +- cyral/core/default_context_handler.go | 30 +- cyral/internal/datalabel/resource.go | 8 +- .../data_source_cyral_saml_configuration.go | 13 +- ...ta_source_cyral_saml_configuration_test.go | 2 +- .../data_source_cyral_sidecar_cft_template.go | 2 +- .../data_source_cyral_sidecar_instance_ids.go | 2 +- .../internal/integration/awsiam/constants.go | 9 + cyral/internal/integration/awsiam/model.go | 53 ++ ...ral_integration_aws_iam.go => resource.go} | 54 +- ...ation_aws_iam_test.go => resource_test.go} | 0 .../integration/awsiam/schema_loader.go | 24 + .../integration/confextension/constants.go | 9 + .../confextension/mfaduo/constants.go | 5 + ...ral_integration_mfa_duo.go => resource.go} | 17 +- ...ation_mfa_duo_test.go => resource_test.go} | 0 .../confextension/mfaduo/schema_loader.go | 24 + ..._integration_confextension.go => model.go} | 26 +- .../confextension/pagerduty/constants.go | 5 + ..._integration_pager_duty.go => resource.go} | 17 +- ...on_pager_duty_test.go => resource_test.go} | 0 .../confextension/pagerduty/schema_loader.go | 24 + .../integration/confextension/resource.go | 108 ++++ .../integration/hcvault/schema_loader.go | 2 +- .../internal/integration/idpsaml/constants.go | 6 + ..._integration_idp_saml.go => datasource.go} | 2 +- ...on_idp_saml_test.go => datasource_test.go} | 0 .../integration/idpsaml/draft/constants.go | 5 + .../integration/idpsaml/draft/model.go | 64 ++ .../resource.go} | 125 +--- .../resource_test.go} | 2 +- .../idpsaml/draft/schema_loader.go | 24 + ...tegration_idp_generic_saml.go => model.go} | 0 ...al_integration_idp_saml.go => resource.go} | 2 +- ...tion_idp_saml_test.go => resource_test.go} | 0 .../integration/idpsaml/schema_loader.go | 29 + .../internal/integration/logging/constants.go | 24 + ...l_integration_logging.go => datasource.go} | 31 +- ...ion_logging_test.go => datasource_test.go} | 0 ...{model_integration_logging.go => model.go} | 202 ++++++- .../internal/integration/logging/resource.go | 35 ++ .../resource_cyral_integration_logging.go | 237 -------- ...ation_logging_test.go => resource_test.go} | 0 .../integration/logging/schema_loader.go | 29 + .../integration/slack/schema_loader.go | 2 +- .../integration/teams/schema_loader.go | 2 +- cyral/internal/permission/constants.go | 10 + ...urce_cyral_permission.go => datasource.go} | 21 +- ..._permission_test.go => datasource_test.go} | 0 .../{model_permission.go => model.go} | 13 + cyral/internal/permission/schema_loader.go | 26 + cyral/internal/policy/constants.go | 5 + cyral/internal/policy/model.go | 109 ++++ cyral/internal/policy/model_policy.go | 28 - .../{resource_cyral_policy.go => resource.go} | 174 +----- ..._cyral_policy_test.go => resource_test.go} | 0 cyral/internal/policy/rule/constants.go | 5 + cyral/internal/policy/rule/model.go | 253 ++++++++ cyral/internal/policy/rule/resource.go | 277 +++++++++ .../policy/rule/resource_cyral_policy_rule.go | 568 ------------------ ...l_policy_rule_test.go => resource_test.go} | 0 cyral/internal/policy/rule/schema_loader.go | 26 + cyral/internal/policy/schema_loader.go | 26 + cyral/internal/regopolicy/constants.go | 26 + ...model_rego_policy_instance.go => model.go} | 0 ...al_rego_policy_instance.go => resource.go} | 100 +-- ...licy_instance_test.go => resource_test.go} | 0 cyral/internal/regopolicy/schema_loader.go | 26 + .../repository/accessgateway/schema_loader.go | 2 +- .../repository/accessrules/schema_loader.go | 2 +- cyral/internal/repository/binding/resource.go | 2 +- .../repository/binding/schema_loader.go | 2 +- .../repository/confanalysis/resource.go | 2 +- .../repository/confanalysis/schema_loader.go | 2 +- .../internal/repository/confauth/resource.go | 2 +- .../repository/confauth/schema_loader.go | 2 +- .../repository/datamap/schema_loader.go | 2 +- cyral/internal/repository/datasource.go | 2 +- cyral/internal/repository/network/resource.go | 2 +- .../repository/network/schema_loader.go | 2 +- .../repository/useraccount/schema_loader.go | 2 +- cyral/internal/role/constants.go | 23 + ...ata_source_cyral_role.go => datasource.go} | 15 +- ..._cyral_role_test.go => datasource_test.go} | 0 cyral/internal/role/model.go | 20 + cyral/internal/role/model_role.go | 103 ---- .../{resource_cyral_role.go => resource.go} | 103 +++- ...ce_cyral_role_test.go => resource_test.go} | 0 cyral/internal/role/schema_loader.go | 29 + cyral/internal/role/ssogroups/constants.go | 5 + cyral/internal/role/ssogroups/model.go | 98 +++ .../resource.go} | 148 +---- .../resource_test.go} | 6 +- .../internal/role/ssogroups/schema_loader.go | 24 + cyral/internal/serviceaccount/constants.go | 11 + .../{model_service_account.go => model.go} | 2 +- cyral/internal/serviceaccount/resource.go | 77 +++ .../resource_cyral_service_account.go | 134 ----- ...rvice_account_test.go => resource_test.go} | 0 .../internal/serviceaccount/schema_loader.go | 26 + .../sidecar/credentials/schema_loader.go | 2 +- cyral/internal/sidecar/health/constants.go | 5 + ..._cyral_sidecar_health.go => datasource.go} | 16 +- ...ecar_health_test.go => datasource_test.go} | 0 cyral/internal/sidecar/health/model.go | 18 + .../internal/sidecar/health/schema_loader.go | 26 + cyral/internal/sidecar/instance/constants.go | 23 + ...yral_sidecar_instance.go => datasource.go} | 22 +- ...ar_instance_test.go => datasource_test.go} | 0 .../{model_sidecar_instance.go => model.go} | 0 .../sidecar/instance/schema_loader.go | 26 + .../sidecar/instance/stats/constants.go | 12 + .../datasource.go} | 26 +- .../datasource_test.go} | 6 +- .../internal/sidecar/instance/stats/model.go | 18 + .../sidecar/instance/stats/schema_loader.go | 26 + cyral/internal/sidecar/listener/datasource.go | 2 +- cyral/internal/sidecar/listener/resource.go | 2 +- .../sidecar/listener/schema_loader.go | 2 +- cyral/internal/systeminfo/constants.go | 11 + ...rce_cyral_system_info.go => datasource.go} | 24 +- ...system_info_test.go => datasource_test.go} | 0 cyral/internal/systeminfo/model.go | 19 + cyral/internal/systeminfo/schema_loader.go | 26 + cyral/provider/provider.go | 47 +- cyral/provider/schema_loader.go | 86 ++- docs/data-sources/saml_configuration.md | 7 +- docs/data-sources/sidecar_cft_template.md | 2 +- docs/data-sources/sidecar_health.md | 4 +- docs/data-sources/sidecar_instance_ids.md | 4 +- 130 files changed, 2365 insertions(+), 1899 deletions(-) rename cyral/internal/{samlconfiguration => deprecated}/data_source_cyral_saml_configuration.go (95%) rename cyral/internal/{samlconfiguration => deprecated}/data_source_cyral_saml_configuration_test.go (99%) create mode 100644 cyral/internal/integration/awsiam/constants.go create mode 100644 cyral/internal/integration/awsiam/model.go rename cyral/internal/integration/awsiam/{resource_cyral_integration_aws_iam.go => resource.go} (54%) rename cyral/internal/integration/awsiam/{resource_cyral_integration_aws_iam_test.go => resource_test.go} (100%) create mode 100644 cyral/internal/integration/awsiam/schema_loader.go create mode 100644 cyral/internal/integration/confextension/constants.go create mode 100644 cyral/internal/integration/confextension/mfaduo/constants.go rename cyral/internal/integration/confextension/mfaduo/{resource_cyral_integration_mfa_duo.go => resource.go} (66%) rename cyral/internal/integration/confextension/mfaduo/{resource_cyral_integration_mfa_duo_test.go => resource_test.go} (100%) create mode 100644 cyral/internal/integration/confextension/mfaduo/schema_loader.go rename cyral/internal/integration/confextension/{model_integration_confextension.go => model.go} (81%) create mode 100644 cyral/internal/integration/confextension/pagerduty/constants.go rename cyral/internal/integration/confextension/pagerduty/{resource_cyral_integration_pager_duty.go => resource.go} (51%) rename cyral/internal/integration/confextension/pagerduty/{resource_cyral_integration_pager_duty_test.go => resource_test.go} (100%) create mode 100644 cyral/internal/integration/confextension/pagerduty/schema_loader.go create mode 100644 cyral/internal/integration/confextension/resource.go create mode 100644 cyral/internal/integration/idpsaml/constants.go rename cyral/internal/integration/idpsaml/{data_source_cyral_integration_idp_saml.go => datasource.go} (99%) rename cyral/internal/integration/idpsaml/{data_source_cyral_integration_idp_saml_test.go => datasource_test.go} (100%) create mode 100644 cyral/internal/integration/idpsaml/draft/constants.go create mode 100644 cyral/internal/integration/idpsaml/draft/model.go rename cyral/internal/integration/idpsaml/{resource_cyral_integration_idp_saml_draft.go => draft/resource.go} (66%) rename cyral/internal/integration/idpsaml/{resource_cyral_integration_idp_saml_draft_test.go => draft/resource_test.go} (99%) create mode 100644 cyral/internal/integration/idpsaml/draft/schema_loader.go rename cyral/internal/integration/idpsaml/{model_integration_idp_generic_saml.go => model.go} (100%) rename cyral/internal/integration/idpsaml/{resource_cyral_integration_idp_saml.go => resource.go} (99%) rename cyral/internal/integration/idpsaml/{resource_cyral_integration_idp_saml_test.go => resource_test.go} (100%) create mode 100644 cyral/internal/integration/idpsaml/schema_loader.go create mode 100644 cyral/internal/integration/logging/constants.go rename cyral/internal/integration/logging/{data_source_cyral_integration_logging.go => datasource.go} (70%) rename cyral/internal/integration/logging/{data_source_cyral_integration_logging_test.go => datasource_test.go} (100%) rename cyral/internal/integration/logging/{model_integration_logging.go => model.go} (56%) create mode 100644 cyral/internal/integration/logging/resource.go delete mode 100644 cyral/internal/integration/logging/resource_cyral_integration_logging.go rename cyral/internal/integration/logging/{resource_cyral_integration_logging_test.go => resource_test.go} (100%) create mode 100644 cyral/internal/integration/logging/schema_loader.go create mode 100644 cyral/internal/permission/constants.go rename cyral/internal/permission/{data_source_cyral_permission.go => datasource.go} (78%) rename cyral/internal/permission/{data_source_cyral_permission_test.go => datasource_test.go} (100%) rename cyral/internal/permission/{model_permission.go => model.go} (67%) create mode 100644 cyral/internal/permission/schema_loader.go create mode 100644 cyral/internal/policy/constants.go create mode 100644 cyral/internal/policy/model.go delete mode 100644 cyral/internal/policy/model_policy.go rename cyral/internal/policy/{resource_cyral_policy.go => resource.go} (52%) rename cyral/internal/policy/{resource_cyral_policy_test.go => resource_test.go} (100%) create mode 100644 cyral/internal/policy/rule/constants.go create mode 100644 cyral/internal/policy/rule/model.go create mode 100644 cyral/internal/policy/rule/resource.go delete mode 100644 cyral/internal/policy/rule/resource_cyral_policy_rule.go rename cyral/internal/policy/rule/{resource_cyral_policy_rule_test.go => resource_test.go} (100%) create mode 100644 cyral/internal/policy/rule/schema_loader.go create mode 100644 cyral/internal/policy/schema_loader.go create mode 100644 cyral/internal/regopolicy/constants.go rename cyral/internal/regopolicy/{model_rego_policy_instance.go => model.go} (100%) rename cyral/internal/regopolicy/{resource_cyral_rego_policy_instance.go => resource.go} (71%) rename cyral/internal/regopolicy/{resource_cyral_rego_policy_instance_test.go => resource_test.go} (100%) create mode 100644 cyral/internal/regopolicy/schema_loader.go create mode 100644 cyral/internal/role/constants.go rename cyral/internal/role/{data_source_cyral_role.go => datasource.go} (92%) rename cyral/internal/role/{data_source_cyral_role_test.go => datasource_test.go} (100%) create mode 100644 cyral/internal/role/model.go delete mode 100644 cyral/internal/role/model_role.go rename cyral/internal/role/{resource_cyral_role.go => resource.go} (63%) rename cyral/internal/role/{resource_cyral_role_test.go => resource_test.go} (100%) create mode 100644 cyral/internal/role/schema_loader.go create mode 100644 cyral/internal/role/ssogroups/constants.go create mode 100644 cyral/internal/role/ssogroups/model.go rename cyral/internal/role/{resource_cyral_role_sso_groups.go => ssogroups/resource.go} (62%) rename cyral/internal/role/{resource_cyral_role_sso_groups_test.go => ssogroups/resource_test.go} (98%) create mode 100644 cyral/internal/role/ssogroups/schema_loader.go create mode 100644 cyral/internal/serviceaccount/constants.go rename cyral/internal/serviceaccount/{model_service_account.go => model.go} (94%) create mode 100644 cyral/internal/serviceaccount/resource.go delete mode 100644 cyral/internal/serviceaccount/resource_cyral_service_account.go rename cyral/internal/serviceaccount/{resource_cyral_service_account_test.go => resource_test.go} (100%) create mode 100644 cyral/internal/serviceaccount/schema_loader.go create mode 100644 cyral/internal/sidecar/health/constants.go rename cyral/internal/sidecar/health/{data_source_cyral_sidecar_health.go => datasource.go} (80%) rename cyral/internal/sidecar/health/{data_source_cyral_sidecar_health_test.go => datasource_test.go} (100%) create mode 100644 cyral/internal/sidecar/health/model.go create mode 100644 cyral/internal/sidecar/health/schema_loader.go create mode 100644 cyral/internal/sidecar/instance/constants.go rename cyral/internal/sidecar/instance/{data_source_cyral_sidecar_instance.go => datasource.go} (90%) rename cyral/internal/sidecar/instance/{data_source_cyral_sidecar_instance_test.go => datasource_test.go} (100%) rename cyral/internal/sidecar/instance/{model_sidecar_instance.go => model.go} (100%) create mode 100644 cyral/internal/sidecar/instance/schema_loader.go create mode 100644 cyral/internal/sidecar/instance/stats/constants.go rename cyral/internal/sidecar/instance/{data_source_cyral_sidecar_instance_stats.go => stats/datasource.go} (76%) rename cyral/internal/sidecar/instance/{data_source_cyral_sidecar_instance_stats_test.go => stats/datasource_test.go} (97%) create mode 100644 cyral/internal/sidecar/instance/stats/model.go create mode 100644 cyral/internal/sidecar/instance/stats/schema_loader.go create mode 100644 cyral/internal/systeminfo/constants.go rename cyral/internal/systeminfo/{data_source_cyral_system_info.go => datasource.go} (69%) rename cyral/internal/systeminfo/{data_source_cyral_system_info_test.go => datasource_test.go} (100%) create mode 100644 cyral/internal/systeminfo/model.go create mode 100644 cyral/internal/systeminfo/schema_loader.go diff --git a/cyral/core/README.md b/cyral/core/README.md index 8108faf4..73fce601 100644 --- a/cyral/core/README.md +++ b/cyral/core/README.md @@ -65,7 +65,7 @@ func (r *NewFeature) ReadFromSchema(d *schema.ResourceData) error { ### datasource.go -Use the `GetPutDeleteURLFactory` to provide the URL factory to read the data source from the API. +Use the `ReadUpdateDeleteURLFactory` to provide the URL factory to read the data source from the API. ```go // datasource.go @@ -75,7 +75,7 @@ var dsContextHandler = core.DefaultContextHandler{ ResourceName: dataSourceName, ResourceType: resourcetype.DataSource, SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &NewFeature{} }, - GetPutDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { + ReadUpdateDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/NewFeature/%s", c.ControlPlane, d.Get("my_id_field").(string)) }, } diff --git a/cyral/core/default_context_handler.go b/cyral/core/default_context_handler.go index 02064dc6..616dbc23 100644 --- a/cyral/core/default_context_handler.go +++ b/cyral/core/default_context_handler.go @@ -20,15 +20,16 @@ import ( // an `id` field, meaning it will use the // `IDBasedResponse` struct in such cases. // 3. `BaseURLFactory` must be provided for resources. It will be used to -// create the POST endpoint and others in case `GetPutDeleteURLFactory` +// create the POST endpoint and others in case `ReadUpdateDeleteURLFactory` // is not provided. -// 4. `GetPutDeleteURLFactory` must be provided for data sources. -// 5. If `GetPutDeleteURLFactory` is NOT provided (data sources or resources), -// the endpoint to perform GET, PUT and DELETE calls are composed by the +// 4. `ReadUpdateDeleteURLFactory` must be provided for data sources. +// 5. If `ReadUpdateDeleteURLFactory` is NOT provided (data sources or resources), +// the endpoints to perform GET, PUT, PATCH and DELETE calls are composed by the // `BaseURLFactory` endpoint plus the ID specification as follows: // - POST: https://// // - GET: https:///// // - PUT: https:///// +// - PATCH: https:///// // - DELETE: https:///// type DefaultContextHandler struct { ResourceName string @@ -41,10 +42,13 @@ type DefaultContextHandler struct { // written in POST operations. SchemaWriterFactoryPostMethod SchemaWriterFactoryFunc // BaseURLFactory provides the URL used for POSTs and that - // will also be used to compose the ID URL for GET, PUT and - // DELETE in case `GetPutDeleteURLFactory` is not provided. - BaseURLFactory URLFactoryFunc - GetPutDeleteURLFactory URLFactoryFunc + // will also be used to compose the ID URL for GET, PUT/PATCH and + // DELETE in case `ReadUpdateDeleteURLFactory` is not provided. + BaseURLFactory URLFactoryFunc + ReadUpdateDeleteURLFactory URLFactoryFunc + + // Http method for update operations. If not provided, assumes http.MethodPut + UpdateMethod string } func DefaultSchemaWriterFactory(d *schema.ResourceData) SchemaWriter { @@ -64,8 +68,8 @@ func (dch DefaultContextHandler) defaultOperationHandler( var url string if httpMethod == http.MethodPost { url = dch.BaseURLFactory(d, c) - } else if dch.GetPutDeleteURLFactory != nil { - url = dch.GetPutDeleteURLFactory(d, c) + } else if dch.ReadUpdateDeleteURLFactory != nil { + url = dch.ReadUpdateDeleteURLFactory(d, c) } else { url = fmt.Sprintf("%s/%s", dch.BaseURLFactory(d, c), d.Id()) } @@ -122,8 +126,12 @@ func (dch DefaultContextHandler) UpdateContext() schema.UpdateContextFunc { func (dch DefaultContextHandler) UpdateContextCustomErrorHandling(getErrorHandler RequestErrorHandler, putErrorHandler RequestErrorHandler) schema.UpdateContextFunc { + updateMethod := http.MethodPut + if dch.UpdateMethod != "" { + updateMethod = dch.UpdateMethod + } return UpdateResource( - dch.defaultOperationHandler(ot.Update, http.MethodPut, dch.SchemaReaderFactory, nil, putErrorHandler), + dch.defaultOperationHandler(ot.Update, updateMethod, dch.SchemaReaderFactory, nil, putErrorHandler), dch.defaultOperationHandler(ot.Update, http.MethodGet, nil, dch.SchemaWriterFactoryGetMethod, getErrorHandler), ) } diff --git a/cyral/internal/datalabel/resource.go b/cyral/internal/datalabel/resource.go index 46a58437..b03cfa58 100644 --- a/cyral/internal/datalabel/resource.go +++ b/cyral/internal/datalabel/resource.go @@ -15,7 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) -var getPutDeleteURLFactory = func(d *schema.ResourceData, c *client.Client) string { +var readUpdateDeleteURLFactory = func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/datalabels/%s", c.ControlPlane, d.Get("name").(string)) @@ -27,7 +27,7 @@ func resourceSchema() *schema.Resource { ResourceType: resourcetype.Resource, SchemaReaderFactory: func() core.SchemaReader { return &DataLabel{} }, SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &DataLabel{} }, - GetPutDeleteURLFactory: getPutDeleteURLFactory, + ReadUpdateDeleteURLFactory: readUpdateDeleteURLFactory, } return &schema.Resource{ Description: "Manages data labels. Data labels are part of the Cyral [Data Map](https://cyral.com/docs/policy/datamap).", @@ -36,7 +36,7 @@ func resourceSchema() *schema.Resource { ResourceName: resourceName, Type: operationtype.Create, HttpMethod: http.MethodPut, - URLFactory: getPutDeleteURLFactory, + URLFactory: readUpdateDeleteURLFactory, SchemaReaderFactory: func() core.SchemaReader { return &DataLabel{} }, SchemaWriterFactory: func(_ *schema.ResourceData) core.SchemaWriter { return &DataLabel{} }, }, readDataLabelConfig, @@ -116,7 +116,7 @@ var readDataLabelConfig = core.ResourceOperationConfig{ ResourceName: resourceName, Type: operationtype.Read, HttpMethod: http.MethodGet, - URLFactory: getPutDeleteURLFactory, + URLFactory: readUpdateDeleteURLFactory, SchemaWriterFactory: func(_ *schema.ResourceData) core.SchemaWriter { return &DataLabel{} }, RequestErrorHandler: &core.IgnoreHttpNotFound{ResName: "Data Label"}, } diff --git a/cyral/internal/samlconfiguration/data_source_cyral_saml_configuration.go b/cyral/internal/deprecated/data_source_cyral_saml_configuration.go similarity index 95% rename from cyral/internal/samlconfiguration/data_source_cyral_saml_configuration.go rename to cyral/internal/deprecated/data_source_cyral_saml_configuration.go index 3a36a234..c7e2eabb 100644 --- a/cyral/internal/samlconfiguration/data_source_cyral_saml_configuration.go +++ b/cyral/internal/deprecated/data_source_cyral_saml_configuration.go @@ -1,4 +1,4 @@ -package samlconfiguration +package deprecated import ( "context" @@ -7,7 +7,6 @@ import ( "net/http" "github.com/cyralinc/terraform-provider-cyral/cyral/client" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/deprecated" "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-log/tflog" @@ -21,6 +20,8 @@ var ( func DataSourceSAMLConfiguration() *schema.Resource { return &schema.Resource{ + DeprecationMessage: "This data source has been deprecated. It will be removed in the next major version of " + + "the provider.", Description: "Parses a SAML metadata URL or a Base64 document into a SAML configuration." + "\n\nSee also the remaining SAML-related resources and data sources.", ReadContext: dataSourceSAMLConfigurationRead, @@ -182,7 +183,7 @@ func dataSourceSAMLConfigurationRead(ctx context.Context, d *schema.ResourceData return utils.CreateError("Unable to retrieve saml configuration", fmt.Sprintf("%v", err)) } - response := deprecated.SAMLConfiguration{} + response := SAMLConfiguration{} if err := json.Unmarshal(body, &response); err != nil { return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) } @@ -197,14 +198,14 @@ func dataSourceSAMLConfigurationRead(ctx context.Context, d *schema.ResourceData return diag.Diagnostics{} } -func getSAMLMetadataRequestFromSchema(d *schema.ResourceData) deprecated.ParseSAMLMetadataRequest { - return deprecated.ParseSAMLMetadataRequest{ +func getSAMLMetadataRequestFromSchema(d *schema.ResourceData) ParseSAMLMetadataRequest { + return ParseSAMLMetadataRequest{ SamlMetadataURL: d.Get("saml_metadata_url").(string), Base64SamlMetadataDocument: d.Get("base_64_saml_metadata_document").(string), } } -func setSAMLConfigurationToSchema(d *schema.ResourceData, data deprecated.SAMLConfiguration) { +func setSAMLConfigurationToSchema(d *schema.ResourceData, data SAMLConfiguration) { if data.Config != nil { d.Set("disable_using_jwks_url", data.Config.DisableUsingJWKSUrl) d.Set("sync_mode", data.Config.SyncMode) diff --git a/cyral/internal/samlconfiguration/data_source_cyral_saml_configuration_test.go b/cyral/internal/deprecated/data_source_cyral_saml_configuration_test.go similarity index 99% rename from cyral/internal/samlconfiguration/data_source_cyral_saml_configuration_test.go rename to cyral/internal/deprecated/data_source_cyral_saml_configuration_test.go index c3c9f9ef..6d52e502 100644 --- a/cyral/internal/samlconfiguration/data_source_cyral_saml_configuration_test.go +++ b/cyral/internal/deprecated/data_source_cyral_saml_configuration_test.go @@ -1,4 +1,4 @@ -package samlconfiguration_test +package deprecated_test import ( "fmt" diff --git a/cyral/internal/deprecated/data_source_cyral_sidecar_cft_template.go b/cyral/internal/deprecated/data_source_cyral_sidecar_cft_template.go index dd6792ff..5dfac420 100644 --- a/cyral/internal/deprecated/data_source_cyral_sidecar_cft_template.go +++ b/cyral/internal/deprecated/data_source_cyral_sidecar_cft_template.go @@ -19,7 +19,7 @@ const CloudFormationDeploymentMethod = "cft-ec2" func DataSourceSidecarCftTemplate() *schema.Resource { return &schema.Resource{ - DeprecationMessage: "This data source was deprecated. It will be removed in the next major version of " + + DeprecationMessage: "This data source has been deprecated. It will be removed in the next major version of " + "the provider and no longer works for control planes `v4.13` and later.", Description: "Retrieves the CloudFormation deployment template for a given sidecar. This data source only " + "supports sidecars with `cft-ec2` deployment method. For Terraform template, use our " + diff --git a/cyral/internal/deprecated/data_source_cyral_sidecar_instance_ids.go b/cyral/internal/deprecated/data_source_cyral_sidecar_instance_ids.go index 71a80b22..63d88557 100644 --- a/cyral/internal/deprecated/data_source_cyral_sidecar_instance_ids.go +++ b/cyral/internal/deprecated/data_source_cyral_sidecar_instance_ids.go @@ -25,7 +25,7 @@ type DeprecatedSidecarInstances struct { func DataSourceSidecarInstanceIDs() *schema.Resource { return &schema.Resource{ - DeprecationMessage: "This data source was deprecated. It will be removed in the next major version of " + + DeprecationMessage: "This data source has been deprecated. It will be removed in the next major version of " + "the provider. Use the data source `cyral_sidecar_instance` instead", Description: "Retrieves the IDs of all the current instances of a given sidecar.", ReadContext: dataSourceSidecarInstanceIDsRead, diff --git a/cyral/internal/integration/awsiam/constants.go b/cyral/internal/integration/awsiam/constants.go new file mode 100644 index 00000000..60a7f7fd --- /dev/null +++ b/cyral/internal/integration/awsiam/constants.go @@ -0,0 +1,9 @@ +package awsiam + +const ( + resourceName = "cyral_integration_aws_iam" + + AWSIAMIntegrationNameKey = "name" + AWSIAMIntegratioNDescriptionKey = "description" + AWSIAMIntegrationARNsKey = "role_arns" +) diff --git a/cyral/internal/integration/awsiam/model.go b/cyral/internal/integration/awsiam/model.go new file mode 100644 index 00000000..c4dc6c5b --- /dev/null +++ b/cyral/internal/integration/awsiam/model.go @@ -0,0 +1,53 @@ +package awsiam + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type AWSIAMIntegrationWrapper struct { + Integration *AWSIAMIntegration `json:"iamIntegration"` +} + +type AWSIAMIntegration struct { + ID string `json:"id,omitempty"` + Name string `json:"name"` + Description string `json:"description"` + IAMRoleARNs []string `json:"iamRoleARNs"` +} + +func (wrapper AWSIAMIntegrationWrapper) WriteToSchema(d *schema.ResourceData) error { + integration := wrapper.Integration + + d.SetId(integration.ID) + + if err := d.Set(AWSIAMIntegrationNameKey, integration.Name); err != nil { + return fmt.Errorf("error setting '%s': %w", AWSIAMIntegrationNameKey, err) + } + + if err := d.Set(AWSIAMIntegratioNDescriptionKey, integration.Description); err != nil { + return fmt.Errorf("error setting '%s': %w", AWSIAMIntegratioNDescriptionKey, err) + } + + if err := d.Set(AWSIAMIntegrationARNsKey, integration.IAMRoleARNs); err != nil { + return fmt.Errorf("error setting '%s': %w", AWSIAMIntegrationARNsKey, err) + } + return nil +} + +func (wrapper *AWSIAMIntegrationWrapper) ReadFromSchema(d *schema.ResourceData) error { + wrapper.Integration = &AWSIAMIntegration{} + + wrapper.Integration.Name = d.Get(AWSIAMIntegrationNameKey).(string) + wrapper.Integration.Description = d.Get(AWSIAMIntegratioNDescriptionKey).(string) + + arns := d.Get(AWSIAMIntegrationARNsKey).([]interface{}) + stringARNs := make([]string, 0, len(arns)) + for _, arn := range arns { + stringARNs = append(stringARNs, arn.(string)) + } + + wrapper.Integration.IAMRoleARNs = stringARNs + return nil +} diff --git a/cyral/internal/integration/awsiam/resource_cyral_integration_aws_iam.go b/cyral/internal/integration/awsiam/resource.go similarity index 54% rename from cyral/internal/integration/awsiam/resource_cyral_integration_aws_iam.go rename to cyral/internal/integration/awsiam/resource.go index 81eaaeed..25a34e82 100644 --- a/cyral/internal/integration/awsiam/resource_cyral_integration_aws_iam.go +++ b/cyral/internal/integration/awsiam/resource.go @@ -10,59 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -const ( - AWSIAMIntegrationNameKey = "name" - AWSIAMIntegratioNDescriptionKey = "description" - AWSIAMIntegrationARNsKey = "role_arns" -) - -type AWSIAMIntegrationWrapper struct { - Integration *AWSIAMIntegration `json:"iamIntegration"` -} - -type AWSIAMIntegration struct { - ID string `json:"id,omitempty"` - Name string `json:"name"` - Description string `json:"description"` - IAMRoleARNs []string `json:"iamRoleARNs"` -} - -func (wrapper *AWSIAMIntegrationWrapper) WriteToSchema(d *schema.ResourceData) error { - integration := wrapper.Integration - - d.SetId(integration.ID) - - if err := d.Set(AWSIAMIntegrationNameKey, integration.Name); err != nil { - return fmt.Errorf("error setting '%s': %w", AWSIAMIntegrationNameKey, err) - } - - if err := d.Set(AWSIAMIntegratioNDescriptionKey, integration.Description); err != nil { - return fmt.Errorf("error setting '%s': %w", AWSIAMIntegratioNDescriptionKey, err) - } - - if err := d.Set(AWSIAMIntegrationARNsKey, integration.IAMRoleARNs); err != nil { - return fmt.Errorf("error setting '%s': %w", AWSIAMIntegrationARNsKey, err) - } - return nil -} - -func (wrapper *AWSIAMIntegrationWrapper) ReadFromSchema(d *schema.ResourceData) error { - wrapper.Integration = &AWSIAMIntegration{} - - wrapper.Integration.Name = d.Get(AWSIAMIntegrationNameKey).(string) - wrapper.Integration.Description = d.Get(AWSIAMIntegratioNDescriptionKey).(string) - - arns := d.Get(AWSIAMIntegrationARNsKey).([]interface{}) - stringARNs := make([]string, 0, len(arns)) - for _, arn := range arns { - stringARNs = append(stringARNs, arn.(string)) - } - - wrapper.Integration.IAMRoleARNs = stringARNs - return nil -} - -func ResourceIntegrationAWSIAM() *schema.Resource { +func resourceSchema() *schema.Resource { contextHandler := core.DefaultContextHandler{ ResourceName: "AWS IAM Integration", ResourceType: resourcetype.Resource, diff --git a/cyral/internal/integration/awsiam/resource_cyral_integration_aws_iam_test.go b/cyral/internal/integration/awsiam/resource_test.go similarity index 100% rename from cyral/internal/integration/awsiam/resource_cyral_integration_aws_iam_test.go rename to cyral/internal/integration/awsiam/resource_test.go diff --git a/cyral/internal/integration/awsiam/schema_loader.go b/cyral/internal/integration/awsiam/schema_loader.go new file mode 100644 index 00000000..b9da8006 --- /dev/null +++ b/cyral/internal/integration/awsiam/schema_loader.go @@ -0,0 +1,24 @@ +package awsiam + +import "github.com/cyralinc/terraform-provider-cyral/cyral/core" + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "integration.awsiam" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: resourceName, + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/integration/confextension/constants.go b/cyral/internal/integration/confextension/constants.go new file mode 100644 index 00000000..0744276c --- /dev/null +++ b/cyral/internal/integration/confextension/constants.go @@ -0,0 +1,9 @@ +package confextension + +const ( + authorizationPurpose = "authorization" + builtinCategory = "builtin" + + PagerDutyTemplateType = "pagerduty" + DuoMFATemplateType = "duoMfa" +) diff --git a/cyral/internal/integration/confextension/mfaduo/constants.go b/cyral/internal/integration/confextension/mfaduo/constants.go new file mode 100644 index 00000000..9ca1d9c0 --- /dev/null +++ b/cyral/internal/integration/confextension/mfaduo/constants.go @@ -0,0 +1,5 @@ +package mfaduo + +const ( + resourceName = "cyral_integration_mfa_duo" +) diff --git a/cyral/internal/integration/confextension/mfaduo/resource_cyral_integration_mfa_duo.go b/cyral/internal/integration/confextension/mfaduo/resource.go similarity index 66% rename from cyral/internal/integration/confextension/mfaduo/resource_cyral_integration_mfa_duo.go rename to cyral/internal/integration/confextension/mfaduo/resource.go index 9d0fe918..8256cef7 100644 --- a/cyral/internal/integration/confextension/mfaduo/resource_cyral_integration_mfa_duo.go +++ b/cyral/internal/integration/confextension/mfaduo/resource.go @@ -1,23 +1,18 @@ package mfaduo import ( - "github.com/cyralinc/terraform-provider-cyral/cyral/core" ce "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/confextension" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) -func ResourceIntegrationMFADuo() *schema.Resource { +func resourceSchema() *schema.Resource { return &schema.Resource{ - Description: "Manages [integration with Duo MFA](https://cyral.com/docs/mfa/duo).", - CreateContext: core.CreateResource( - ce.ConfExtensionIntegrationCreate(ce.DuoMFATemplateType), - ce.ConfExtensionIntegrationRead(ce.DuoMFATemplateType)), - ReadContext: core.ReadResource(ce.ConfExtensionIntegrationRead(ce.DuoMFATemplateType)), - UpdateContext: core.UpdateResource( - ce.ConfExtensionIntegrationUpdate(ce.DuoMFATemplateType), - ce.ConfExtensionIntegrationRead(ce.DuoMFATemplateType)), - DeleteContext: core.DeleteResource(ce.ConfExtensionIntegrationDelete(ce.DuoMFATemplateType)), + Description: "Manages [integration with Duo MFA](https://cyral.com/docs/mfa/duo).", + CreateContext: ce.CreateResource(resourceName, ce.DuoMFATemplateType), + ReadContext: ce.ReadResource(resourceName, ce.DuoMFATemplateType), + UpdateContext: ce.UpdateResource(resourceName, ce.DuoMFATemplateType), + DeleteContext: ce.DeleteResource(resourceName, ce.DuoMFATemplateType), Schema: map[string]*schema.Schema{ "id": { diff --git a/cyral/internal/integration/confextension/mfaduo/resource_cyral_integration_mfa_duo_test.go b/cyral/internal/integration/confextension/mfaduo/resource_test.go similarity index 100% rename from cyral/internal/integration/confextension/mfaduo/resource_cyral_integration_mfa_duo_test.go rename to cyral/internal/integration/confextension/mfaduo/resource_test.go diff --git a/cyral/internal/integration/confextension/mfaduo/schema_loader.go b/cyral/internal/integration/confextension/mfaduo/schema_loader.go new file mode 100644 index 00000000..57294dd9 --- /dev/null +++ b/cyral/internal/integration/confextension/mfaduo/schema_loader.go @@ -0,0 +1,24 @@ +package mfaduo + +import "github.com/cyralinc/terraform-provider-cyral/cyral/core" + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "integration.confextension.mfaduo" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: resourceName, + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/integration/confextension/model_integration_confextension.go b/cyral/internal/integration/confextension/model.go similarity index 81% rename from cyral/internal/integration/confextension/model_integration_confextension.go rename to cyral/internal/integration/confextension/model.go index 3fa3e8fa..a20cc1e1 100644 --- a/cyral/internal/integration/confextension/model_integration_confextension.go +++ b/cyral/internal/integration/confextension/model.go @@ -12,14 +12,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -const ( - authorizationPurpose = "authorization" - builtinCategory = "builtin" - - PagerDutyTemplateType = "pagerduty" - DuoMFATemplateType = "duoMfa" -) - type IntegrationConfExtension struct { ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` @@ -44,7 +36,7 @@ type IntegrationConfExtensionParameters struct { APIHostname string `json:"apiHostname,omitempty"` } -func (data *IntegrationConfExtension) WriteToSchema(d *schema.ResourceData) error { +func (data IntegrationConfExtension) WriteToSchema(d *schema.ResourceData) error { d.Set("id", data.ID) d.Set("name", data.Name) @@ -89,9 +81,9 @@ func (data *IntegrationConfExtension) ReadFromSchema(d *schema.ResourceData) err return nil } -func ConfExtensionIntegrationCreate(templateType string) core.ResourceOperationConfig { +func ConfExtensionIntegrationCreate(resourceName, templateType string) core.ResourceOperationConfig { return core.ResourceOperationConfig{ - ResourceName: fmt.Sprintf("%s_IntegrationResourceCreate", templateType), + ResourceName: resourceName, Type: operationtype.Create, HttpMethod: http.MethodPost, URLFactory: func(d *schema.ResourceData, c *client.Client) string { @@ -106,9 +98,9 @@ func ConfExtensionIntegrationCreate(templateType string) core.ResourceOperationC } } -func ConfExtensionIntegrationRead(templateType string) core.ResourceOperationConfig { +func ConfExtensionIntegrationRead(resourceName, templateType string) core.ResourceOperationConfig { return core.ResourceOperationConfig{ - ResourceName: fmt.Sprintf("%s_IntegrationResourceRead", templateType), + ResourceName: resourceName, Type: operationtype.Read, HttpMethod: http.MethodGet, URLFactory: func(d *schema.ResourceData, c *client.Client) string { @@ -123,9 +115,9 @@ func ConfExtensionIntegrationRead(templateType string) core.ResourceOperationCon } } -func ConfExtensionIntegrationUpdate(templateType string) core.ResourceOperationConfig { +func ConfExtensionIntegrationUpdate(resourceName, templateType string) core.ResourceOperationConfig { return core.ResourceOperationConfig{ - ResourceName: fmt.Sprintf("%s_IntegrationResourceUpdate", templateType), + ResourceName: resourceName, Type: operationtype.Update, HttpMethod: http.MethodPut, URLFactory: func(d *schema.ResourceData, c *client.Client) string { @@ -139,9 +131,9 @@ func ConfExtensionIntegrationUpdate(templateType string) core.ResourceOperationC } } -func ConfExtensionIntegrationDelete(templateType string) core.ResourceOperationConfig { +func ConfExtensionIntegrationDelete(resourceName, templateType string) core.ResourceOperationConfig { return core.ResourceOperationConfig{ - ResourceName: fmt.Sprintf("%s_IntegrationResourceDelete", templateType), + ResourceName: resourceName, Type: operationtype.Delete, HttpMethod: http.MethodDelete, URLFactory: func(d *schema.ResourceData, c *client.Client) string { diff --git a/cyral/internal/integration/confextension/pagerduty/constants.go b/cyral/internal/integration/confextension/pagerduty/constants.go new file mode 100644 index 00000000..33050987 --- /dev/null +++ b/cyral/internal/integration/confextension/pagerduty/constants.go @@ -0,0 +1,5 @@ +package pagerduty + +const ( + resourceName = "cyral_integration_pager_duty" +) diff --git a/cyral/internal/integration/confextension/pagerduty/resource_cyral_integration_pager_duty.go b/cyral/internal/integration/confextension/pagerduty/resource.go similarity index 51% rename from cyral/internal/integration/confextension/pagerduty/resource_cyral_integration_pager_duty.go rename to cyral/internal/integration/confextension/pagerduty/resource.go index ce4f48ef..7076ad80 100644 --- a/cyral/internal/integration/confextension/pagerduty/resource_cyral_integration_pager_duty.go +++ b/cyral/internal/integration/confextension/pagerduty/resource.go @@ -1,22 +1,17 @@ package pagerduty import ( - "github.com/cyralinc/terraform-provider-cyral/cyral/core" ce "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/confextension" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -func ResourceIntegrationPagerDuty() *schema.Resource { +func resourceSchema() *schema.Resource { return &schema.Resource{ - Description: "Manages [integration with PagerDuty](https://cyral.com/docs/integrations/incident-response/pagerduty/#in-cyral).", - CreateContext: core.CreateResource( - ce.ConfExtensionIntegrationCreate(ce.PagerDutyTemplateType), - ce.ConfExtensionIntegrationRead(ce.PagerDutyTemplateType)), - ReadContext: core.ReadResource(ce.ConfExtensionIntegrationRead(ce.PagerDutyTemplateType)), - UpdateContext: core.UpdateResource( - ce.ConfExtensionIntegrationUpdate(ce.PagerDutyTemplateType), - ce.ConfExtensionIntegrationRead(ce.PagerDutyTemplateType)), - DeleteContext: core.DeleteResource(ce.ConfExtensionIntegrationDelete(ce.PagerDutyTemplateType)), + Description: "Manages [integration with PagerDuty](https://cyral.com/docs/integrations/incident-response/pagerduty/#in-cyral).", + CreateContext: ce.CreateResource(resourceName, ce.PagerDutyTemplateType), + ReadContext: ce.ReadResource(resourceName, ce.PagerDutyTemplateType), + UpdateContext: ce.UpdateResource(resourceName, ce.PagerDutyTemplateType), + DeleteContext: ce.DeleteResource(resourceName, ce.PagerDutyTemplateType), Schema: map[string]*schema.Schema{ "id": { diff --git a/cyral/internal/integration/confextension/pagerduty/resource_cyral_integration_pager_duty_test.go b/cyral/internal/integration/confextension/pagerduty/resource_test.go similarity index 100% rename from cyral/internal/integration/confextension/pagerduty/resource_cyral_integration_pager_duty_test.go rename to cyral/internal/integration/confextension/pagerduty/resource_test.go diff --git a/cyral/internal/integration/confextension/pagerduty/schema_loader.go b/cyral/internal/integration/confextension/pagerduty/schema_loader.go new file mode 100644 index 00000000..e0632ec0 --- /dev/null +++ b/cyral/internal/integration/confextension/pagerduty/schema_loader.go @@ -0,0 +1,24 @@ +package pagerduty + +import "github.com/cyralinc/terraform-provider-cyral/cyral/core" + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "integration.confextension.pagerduty" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: resourceName, + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/integration/confextension/resource.go b/cyral/internal/integration/confextension/resource.go new file mode 100644 index 00000000..3781e12c --- /dev/null +++ b/cyral/internal/integration/confextension/resource.go @@ -0,0 +1,108 @@ +package confextension + +import ( + "fmt" + "net/http" + + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/operationtype" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func CreateResource(resourceName, templateType string) schema.CreateContextFunc { + return core.CreateResource( + create(resourceName, templateType), + read(resourceName, templateType), + ) +} + +func ReadResource(resourceName, templateType string) schema.ReadContextFunc { + return core.ReadResource(read(resourceName, templateType)) +} + +func UpdateResource(resourceName, templateType string) schema.UpdateContextFunc { + return core.UpdateResource( + update(resourceName, templateType), + read(resourceName, templateType), + ) +} + +func DeleteResource(resourceName, templateType string) schema.DeleteContextFunc { + return core.DeleteResource(delete(resourceName)) +} + +func create(resourceName, templateType string) core.ResourceOperationConfig { + return core.ResourceOperationConfig{ + ResourceName: resourceName, + Type: operationtype.Create, + HttpMethod: http.MethodPost, + URLFactory: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf( + "https://%s/v1/integrations/confExtensions/instances", c.ControlPlane, + ) + }, + SchemaReaderFactory: func() core.SchemaReader { + return NewIntegrationConfExtension(templateType) + }, + SchemaWriterFactory: core.DefaultSchemaWriterFactory, + } +} + +func read(resourceName, templateType string) core.ResourceOperationConfig { + return core.ResourceOperationConfig{ + ResourceName: resourceName, + Type: operationtype.Read, + HttpMethod: http.MethodGet, + URLFactory: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf( + "https://%s/v1/integrations/confExtensions/instances/authorization/%s", + c.ControlPlane, d.Id(), + ) + }, + SchemaWriterFactory: func(_ *schema.ResourceData) core.SchemaWriter { + return NewIntegrationConfExtension(templateType) + }, + RequestErrorHandler: &core.IgnoreNotFoundByMessage{ + ResName: resourceName, + MessageMatches: "not found for key", + OperationType: operationtype.Read, + }, + } +} + +func update(resourceName, templateType string) core.ResourceOperationConfig { + return core.ResourceOperationConfig{ + ResourceName: resourceName, + Type: operationtype.Update, + HttpMethod: http.MethodPut, + URLFactory: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf( + "https://%s/v1/integrations/confExtensions/instances/%s", c.ControlPlane, d.Id(), + ) + }, + SchemaReaderFactory: func() core.SchemaReader { + return NewIntegrationConfExtension(templateType) + }, + } +} + +func delete(resourceName string) core.ResourceOperationConfig { + return core.ResourceOperationConfig{ + ResourceName: resourceName, + Type: operationtype.Delete, + HttpMethod: http.MethodDelete, + URLFactory: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf( + "https://%s/v1/integrations/confExtensions/instances/authorization/%s", + c.ControlPlane, d.Id(), + ) + }, + RequestErrorHandler: &core.IgnoreNotFoundByMessage{ + ResName: resourceName, + MessageMatches: "not found for key", + OperationType: operationtype.Delete, + }, + } +} diff --git a/cyral/internal/integration/hcvault/schema_loader.go b/cyral/internal/integration/hcvault/schema_loader.go index 159aa501..59631f4a 100644 --- a/cyral/internal/integration/hcvault/schema_loader.go +++ b/cyral/internal/integration/hcvault/schema_loader.go @@ -6,7 +6,7 @@ type packageSchema struct { } func (p *packageSchema) Name() string { - return "hcvault" + return "integration.hcvault" } func (p *packageSchema) Schemas() []*core.SchemaDescriptor { diff --git a/cyral/internal/integration/idpsaml/constants.go b/cyral/internal/integration/idpsaml/constants.go new file mode 100644 index 00000000..0f562968 --- /dev/null +++ b/cyral/internal/integration/idpsaml/constants.go @@ -0,0 +1,6 @@ +package idpsaml + +const ( + resourceName = "cyral_integration_idp_saml" + dataSourceName = "cyral_integration_idp_saml" +) diff --git a/cyral/internal/integration/idpsaml/data_source_cyral_integration_idp_saml.go b/cyral/internal/integration/idpsaml/datasource.go similarity index 99% rename from cyral/internal/integration/idpsaml/data_source_cyral_integration_idp_saml.go rename to cyral/internal/integration/idpsaml/datasource.go index 21b8bcc4..3e46231d 100644 --- a/cyral/internal/integration/idpsaml/data_source_cyral_integration_idp_saml.go +++ b/cyral/internal/integration/idpsaml/datasource.go @@ -82,7 +82,7 @@ func dataSourceIntegrationIdPSAMLReadConfig() core.ResourceOperationConfig { } } -func DataSourceIntegrationIdPSAML() *schema.Resource { +func dataSourceSchema() *schema.Resource { return &schema.Resource{ Description: "Retrieve and filter SAML IdP integrations.", ReadContext: core.ReadResource(dataSourceIntegrationIdPSAMLReadConfig()), diff --git a/cyral/internal/integration/idpsaml/data_source_cyral_integration_idp_saml_test.go b/cyral/internal/integration/idpsaml/datasource_test.go similarity index 100% rename from cyral/internal/integration/idpsaml/data_source_cyral_integration_idp_saml_test.go rename to cyral/internal/integration/idpsaml/datasource_test.go diff --git a/cyral/internal/integration/idpsaml/draft/constants.go b/cyral/internal/integration/idpsaml/draft/constants.go new file mode 100644 index 00000000..ce0bb185 --- /dev/null +++ b/cyral/internal/integration/idpsaml/draft/constants.go @@ -0,0 +1,5 @@ +package draft + +const ( + resourceName = "cyral_integration_idp_saml_draft" +) diff --git a/cyral/internal/integration/idpsaml/draft/model.go b/cyral/internal/integration/idpsaml/draft/model.go new file mode 100644 index 00000000..41703fe2 --- /dev/null +++ b/cyral/internal/integration/idpsaml/draft/model.go @@ -0,0 +1,64 @@ +package draft + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/idpsaml" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" +) + +type CreateGenericSAMLDraftRequest struct { + DisplayName string `json:"displayName"` + DisableIdPInitiatedLogin bool `json:"disableIdPInitiatedLogin"` + IdpType string `json:"idpType,omitempty"` + Attributes *idpsaml.RequiredUserAttributes `json:"attributes,omitempty"` +} + +func (req *CreateGenericSAMLDraftRequest) ReadFromSchema(d *schema.ResourceData) error { + req.DisplayName = d.Get("display_name").(string) + req.DisableIdPInitiatedLogin = d.Get("disable_idp_initiated_login").(bool) + req.IdpType = d.Get("idp_type").(string) + + attributes, err := idpsaml.RequiredUserAttributesFromSchema(d) + if err != nil { + return err + } + req.Attributes = attributes + + return nil +} + +type GenericSAMLDraftResponse struct { + Draft idpsaml.GenericSAMLDraft `json:"draft"` +} + +func (resp *GenericSAMLDraftResponse) WriteToSchema(d *schema.ResourceData) error { + d.SetId(resp.Draft.ID) + if err := d.Set("display_name", resp.Draft.DisplayName); err != nil { + return err + } + if err := d.Set("disable_idp_initiated_login", resp.Draft.DisableIdPInitiatedLogin); err != nil { + return err + } + if err := d.Set("sp_metadata", resp.Draft.SPMetadata.XMLDocument); err != nil { + return err + } + if resp.Draft.SPMetadata != nil { + if err := resp.Draft.SPMetadata.WriteToSchema(d); err != nil { + return err + } + } + if err := d.Set("idp_type", resp.Draft.IdpType); err != nil { + return err + } + if resp.Draft.Attributes != nil && utils.TypeSetNonEmpty(d, "attributes") { + if err := resp.Draft.Attributes.WriteToSchema(d); err != nil { + return err + } + } + return nil +} + +type ListGenericSAMLDraftsResponse struct { + Drafts []idpsaml.GenericSAMLDraft `json:"drafts"` +} diff --git a/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_draft.go b/cyral/internal/integration/idpsaml/draft/resource.go similarity index 66% rename from cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_draft.go rename to cyral/internal/integration/idpsaml/draft/resource.go index 61b84443..de0754ec 100644 --- a/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_draft.go +++ b/cyral/internal/integration/idpsaml/draft/resource.go @@ -1,4 +1,4 @@ -package idpsaml +package draft import ( "context" @@ -12,7 +12,8 @@ import ( "github.com/cyralinc/terraform-provider-cyral/cyral/client" "github.com/cyralinc/terraform-provider-cyral/cyral/core" - "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/operationtype" + "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/resourcetype" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/idpsaml" "github.com/cyralinc/terraform-provider-cyral/cyral/utils" ) @@ -32,110 +33,24 @@ import ( // 2. Provide the IdP metadata to the `cyral_integration_idp_saml` resource. // -type CreateGenericSAMLDraftRequest struct { - DisplayName string `json:"displayName"` - DisableIdPInitiatedLogin bool `json:"disableIdPInitiatedLogin"` - IdpType string `json:"idpType,omitempty"` - Attributes *RequiredUserAttributes `json:"attributes,omitempty"` +var resourceContextHandler = core.DefaultContextHandler{ + ResourceName: resourceName, + ResourceType: resourcetype.Resource, + SchemaReaderFactory: func() core.SchemaReader { return &CreateGenericSAMLDraftRequest{} }, + SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &GenericSAMLDraftResponse{} }, + SchemaWriterFactoryPostMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &GenericSAMLDraftResponse{} }, + BaseURLFactory: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/integrations/generic-saml/drafts", c.ControlPlane) + }, } -func (req *CreateGenericSAMLDraftRequest) ReadFromSchema(d *schema.ResourceData) error { - req.DisplayName = d.Get("display_name").(string) - req.DisableIdPInitiatedLogin = d.Get("disable_idp_initiated_login").(bool) - req.IdpType = d.Get("idp_type").(string) - - attributes, err := RequiredUserAttributesFromSchema(d) - if err != nil { - return err - } - req.Attributes = attributes - - return nil -} - -type GenericSAMLDraftResponse struct { - Draft GenericSAMLDraft `json:"draft"` -} - -func (resp *GenericSAMLDraftResponse) WriteToSchema(d *schema.ResourceData) error { - d.SetId(resp.Draft.ID) - if err := d.Set("display_name", resp.Draft.DisplayName); err != nil { - return err - } - if err := d.Set("disable_idp_initiated_login", resp.Draft.DisableIdPInitiatedLogin); err != nil { - return err - } - if err := d.Set("sp_metadata", resp.Draft.SPMetadata.XMLDocument); err != nil { - return err - } - if resp.Draft.SPMetadata != nil { - if err := resp.Draft.SPMetadata.WriteToSchema(d); err != nil { - return err - } - } - if err := d.Set("idp_type", resp.Draft.IdpType); err != nil { - return err - } - if resp.Draft.Attributes != nil && utils.TypeSetNonEmpty(d, "attributes") { - if err := resp.Draft.Attributes.WriteToSchema(d); err != nil { - return err - } - } - return nil -} - -type ListGenericSAMLDraftsResponse struct { - Drafts []GenericSAMLDraft `json:"drafts"` -} - -func CreateGenericSAMLDraftConfig() core.ResourceOperationConfig { - return core.ResourceOperationConfig{ - ResourceName: "GenericSAMLDraftResourceCreate", - Type: operationtype.Create, - HttpMethod: http.MethodPost, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf("https://%s/v1/integrations/generic-saml/drafts", c.ControlPlane) - }, - SchemaReaderFactory: func() core.SchemaReader { return &CreateGenericSAMLDraftRequest{} }, - SchemaWriterFactory: func(_ *schema.ResourceData) core.SchemaWriter { return &GenericSAMLDraftResponse{} }, - } -} - -func ReadGenericSAMLDraftConfig() core.ResourceOperationConfig { - return core.ResourceOperationConfig{ - ResourceName: "GenericSAMLDraftResourceRead", - Type: operationtype.Read, - HttpMethod: http.MethodGet, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf("https://%s/v1/integrations/generic-saml/drafts/%s", c.ControlPlane, d.Id()) - }, - RequestErrorHandler: &readGenericSAMLDraftErrorHandler{}, - SchemaWriterFactory: func(_ *schema.ResourceData) core.SchemaWriter { return &GenericSAMLDraftResponse{} }, - } -} - -func DeleteGenericSAMLDraftConfig() core.ResourceOperationConfig { - return core.ResourceOperationConfig{ - ResourceName: "GenericSAMLDraftResourceDelete", - Type: operationtype.Delete, - HttpMethod: http.MethodDelete, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf("https://%s/v1/integrations/generic-saml/drafts/%s", c.ControlPlane, d.Id()) - }, - RequestErrorHandler: &core.IgnoreHttpNotFound{ResName: "SAML draft"}, - } -} - -func ResourceIntegrationIdPSAMLDraft() *schema.Resource { +func resourceSchema() *schema.Resource { return &schema.Resource{ Description: "Manages SAML IdP integration drafts." + "\n\nSee also the remaining SAML-related resources and data sources.", - CreateContext: core.CreateResource( - CreateGenericSAMLDraftConfig(), - ReadGenericSAMLDraftConfig(), - ), - ReadContext: core.ReadResource(ReadGenericSAMLDraftConfig()), - DeleteContext: core.DeleteResource(DeleteGenericSAMLDraftConfig()), + CreateContext: resourceContextHandler.CreateContextCustomErrorHandling(&readGenericSAMLDraftErrorHandler{}, nil), + ReadContext: resourceContextHandler.ReadContextCustomErrorHandling(&readGenericSAMLDraftErrorHandler{}), + DeleteContext: resourceContextHandler.DeleteContext(), Schema: map[string]*schema.Schema{ // All of the input arguments must force recreation of // the resource, because the API does not support @@ -176,7 +91,7 @@ func ResourceIntegrationIdPSAMLDraft() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - Default: DefaultUserAttributeFirstName, + Default: idpsaml.DefaultUserAttributeFirstName, ValidateFunc: utils.ValidationStringLenAtLeast(3), }, "last_name": { @@ -184,7 +99,7 @@ func ResourceIntegrationIdPSAMLDraft() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - Default: DefaultUserAttributeLastName, + Default: idpsaml.DefaultUserAttributeLastName, ValidateFunc: utils.ValidationStringLenAtLeast(3), }, "email": { @@ -192,7 +107,7 @@ func ResourceIntegrationIdPSAMLDraft() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - Default: DefaultUserAttributeEmail, + Default: idpsaml.DefaultUserAttributeEmail, ValidateFunc: utils.ValidationStringLenAtLeast(3), }, "groups": { @@ -200,7 +115,7 @@ func ResourceIntegrationIdPSAMLDraft() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - Default: DefaultUserAttributeGroups, + Default: idpsaml.DefaultUserAttributeGroups, ValidateFunc: utils.ValidationStringLenAtLeast(3), }, }, diff --git a/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_draft_test.go b/cyral/internal/integration/idpsaml/draft/resource_test.go similarity index 99% rename from cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_draft_test.go rename to cyral/internal/integration/idpsaml/draft/resource_test.go index 602918ba..adef993c 100644 --- a/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_draft_test.go +++ b/cyral/internal/integration/idpsaml/draft/resource_test.go @@ -1,4 +1,4 @@ -package idpsaml_test +package draft_test import ( "fmt" diff --git a/cyral/internal/integration/idpsaml/draft/schema_loader.go b/cyral/internal/integration/idpsaml/draft/schema_loader.go new file mode 100644 index 00000000..552d61c1 --- /dev/null +++ b/cyral/internal/integration/idpsaml/draft/schema_loader.go @@ -0,0 +1,24 @@ +package draft + +import "github.com/cyralinc/terraform-provider-cyral/cyral/core" + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "integration.idpsaml.draft" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: resourceName, + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/integration/idpsaml/model_integration_idp_generic_saml.go b/cyral/internal/integration/idpsaml/model.go similarity index 100% rename from cyral/internal/integration/idpsaml/model_integration_idp_generic_saml.go rename to cyral/internal/integration/idpsaml/model.go diff --git a/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml.go b/cyral/internal/integration/idpsaml/resource.go similarity index 99% rename from cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml.go rename to cyral/internal/integration/idpsaml/resource.go index a3efdf20..569e7f54 100644 --- a/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml.go +++ b/cyral/internal/integration/idpsaml/resource.go @@ -108,7 +108,7 @@ func DeleteGenericSAMLConfig() core.ResourceOperationConfig { } } -func ResourceIntegrationIdPSAML() *schema.Resource { +func resourceSchema() *schema.Resource { return &schema.Resource{ Description: "Manages identity provider (IdP) integrations using SAML to allow " + "[Single Sing-On](https://cyral.com/docs/sso/overview) to Cyral.\n\nSee also " + diff --git a/cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_test.go b/cyral/internal/integration/idpsaml/resource_test.go similarity index 100% rename from cyral/internal/integration/idpsaml/resource_cyral_integration_idp_saml_test.go rename to cyral/internal/integration/idpsaml/resource_test.go diff --git a/cyral/internal/integration/idpsaml/schema_loader.go b/cyral/internal/integration/idpsaml/schema_loader.go new file mode 100644 index 00000000..2d5c6a82 --- /dev/null +++ b/cyral/internal/integration/idpsaml/schema_loader.go @@ -0,0 +1,29 @@ +package idpsaml + +import "github.com/cyralinc/terraform-provider-cyral/cyral/core" + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "integration.idpsaml" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: dataSourceName, + Type: core.DataSourceSchemaType, + Schema: dataSourceSchema, + }, + { + Name: resourceName, + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/integration/logging/constants.go b/cyral/internal/integration/logging/constants.go new file mode 100644 index 00000000..a78bcd73 --- /dev/null +++ b/cyral/internal/integration/logging/constants.go @@ -0,0 +1,24 @@ +package logging + +const ( + resourceName = "cyral_integration_logging" + dataSourceName = "cyral_integration_logging" +) + +const ( + CloudWatchKey = "cloudwatch" + DatadogKey = "datadog" + ElkKey = "elk" + SplunkKey = "splunk" + SumoLogicKey = "sumo_logic" + FluentbitKey = "fluent_bit" +) + +var allLogIntegrationConfigs = []string{ + CloudWatchKey, + DatadogKey, + ElkKey, + SplunkKey, + SumoLogicKey, + FluentbitKey, +} diff --git a/cyral/internal/integration/logging/data_source_cyral_integration_logging.go b/cyral/internal/integration/logging/datasource.go similarity index 70% rename from cyral/internal/integration/logging/data_source_cyral_integration_logging.go rename to cyral/internal/integration/logging/datasource.go index be7031a7..358de4c5 100644 --- a/cyral/internal/integration/logging/data_source_cyral_integration_logging.go +++ b/cyral/internal/integration/logging/datasource.go @@ -8,39 +8,10 @@ import ( "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/operationtype" "github.com/cyralinc/terraform-provider-cyral/cyral/utils" - "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) -type ListIntegrationLogsResponse struct { - Integrations []LoggingIntegration `json:"integrations"` -} - -func (resp *ListIntegrationLogsResponse) WriteToSchema(d *schema.ResourceData) error { - integrations := make([]interface{}, len(resp.Integrations)) - for i, integration := range resp.Integrations { - // write in config scheme - configType, config, err := getLoggingConfig(&integration) - if err != nil { - return err - } - integrations[i] = map[string]interface{}{ - "id": integration.Id, - "name": integration.Name, - "receive_audit_logs": integration.ReceiveAuditLogs, - configType: config, - } - } - if err := d.Set("integrations", integrations); err != nil { - return err - } - - d.SetId(uuid.New().String()) - - return nil -} - func dataSourceIntegrationLogsRead() core.ResourceOperationConfig { return core.ResourceOperationConfig{ ResourceName: "IntegrationLogsDataSourceRead", @@ -56,7 +27,7 @@ func dataSourceIntegrationLogsRead() core.ResourceOperationConfig { } } -func DataSourceIntegrationLogging() *schema.Resource { +func dataSourceSchema() *schema.Resource { rawSchema := getIntegrationLogsSchema() // all fields in integrations are computed. // this function changes the schema to achieve this diff --git a/cyral/internal/integration/logging/data_source_cyral_integration_logging_test.go b/cyral/internal/integration/logging/datasource_test.go similarity index 100% rename from cyral/internal/integration/logging/data_source_cyral_integration_logging_test.go rename to cyral/internal/integration/logging/datasource_test.go diff --git a/cyral/internal/integration/logging/model_integration_logging.go b/cyral/internal/integration/logging/model.go similarity index 56% rename from cyral/internal/integration/logging/model_integration_logging.go rename to cyral/internal/integration/logging/model.go index 4ac8f340..0f53386c 100644 --- a/cyral/internal/integration/logging/model_integration_logging.go +++ b/cyral/internal/integration/logging/model.go @@ -1,6 +1,9 @@ package logging import ( + "fmt" + + "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -58,22 +61,191 @@ type LoggingIntegrationConfig struct { FluentBit *FluentBitConfig `json:"fluentBit"` } -const ( - CloudWatchKey = "cloudwatch" - DatadogKey = "datadog" - ElkKey = "elk" - SplunkKey = "splunk" - SumoLogicKey = "sumo_logic" - FluentbitKey = "fluent_bit" -) +func getLoggingConfig(resource *LoggingIntegration) (string, []interface{}, error) { + var configType string + var configScheme []interface{} + switch { + case resource.CloudWatch != nil: + configType = CloudWatchKey + configScheme = []interface{}{ + map[string]interface{}{ + "region": resource.CloudWatch.Region, + "group": resource.CloudWatch.Group, + "stream": resource.CloudWatch.Stream, + }, + } + case resource.Datadog != nil: + configType = DatadogKey + configScheme = []interface{}{ + map[string]interface{}{ + "api_key": resource.Datadog.ApiKey, + }, + } + case resource.Elk != nil: + configType = ElkKey + elkConfig := map[string]interface{}{ + "es_url": resource.Elk.EsURL, + "kibana_url": resource.Elk.KibanaURL, + } + // Optional, so we need to verify separately + if resource.Elk.EsCredentials != nil { + elkConfig["es_credentials"] = []interface{}{ + map[string]interface{}{ + "username": resource.Elk.EsCredentials.Username, + "password": resource.Elk.EsCredentials.Password, + }, + } + } + configScheme = []interface{}{elkConfig} + case resource.Splunk != nil: + configType = SplunkKey + configScheme = []interface{}{ + map[string]interface{}{ + "hostname": resource.Splunk.Hostname, + "hec_port": resource.Splunk.HecPort, + "access_token": resource.Splunk.AccessToken, + "index": resource.Splunk.Index, + "use_tls": resource.Splunk.UseTLS, + }, + } + case resource.SumoLogic != nil: + configType = SumoLogicKey + configScheme = []interface{}{ + map[string]interface{}{ + "address": resource.SumoLogic.Address, + }, + } + case resource.FluentBit != nil: + configType = FluentbitKey + configScheme = []interface{}{ + map[string]interface{}{ + "config": resource.FluentBit.Config, + "skip_validate": resource.FluentBit.SkipValidate, + }, + } + default: + return configType, nil, fmt.Errorf("config scheme is required, log integration config is corrupt: %v", resource) + } + + return configType, configScheme, nil +} + +func (resource *LoggingIntegration) WriteToSchema(d *schema.ResourceData) error { + if err := d.Set("name", resource.Name); err != nil { + return fmt.Errorf("error setting 'name': %w", err) + } + if err := d.Set("receive_audit_logs", resource.ReceiveAuditLogs); err != nil { + return fmt.Errorf("error setting 'receive_audit_logs': %w", err) + } + + configType, configScheme, err := getLoggingConfig(resource) + if err != nil { + return err + } + + if err := d.Set(configType, configScheme); err != nil { + return fmt.Errorf("error setting 'config': %w", err) + } + + return nil +} + +// ReadFromSchema is used to parse the resource schema into a logging integration structure that is expected by the API +func (integrationLogConfig *LoggingIntegration) ReadFromSchema(d *schema.ResourceData) error { + integrationLogConfig.Id = d.Id() //Get("integration_id").(string) + integrationLogConfig.Name = d.Get("name").(string) + integrationLogConfig.ReceiveAuditLogs = d.Get("receive_audit_logs").(bool) + + // Handle Config Scheme (required field). + var configType string + var config interface{} + for _, integrationType := range allLogIntegrationConfigs { + configAux, isConfigOk := d.GetOk(integrationType) + if isConfigOk { + config = configAux.(interface{}) + configType = integrationType + break + } + } + + configDetails := config.(*schema.Set).List() + + m := configDetails[0].(map[string]interface{}) + + switch configType { + case CloudWatchKey: + integrationLogConfig.CloudWatch = &CloudWatchConfig{ + Region: m["region"].(string), + Group: m["group"].(string), + Stream: m["stream"].(string), + } + case DatadogKey: + integrationLogConfig.Datadog = &DataDogConfig{ + ApiKey: m["api_key"].(string), + } + case ElkKey: + integrationLogConfig.Elk = &ElkConfig{ + EsURL: m["es_url"].(string), + KibanaURL: m["kibana_url"].(string), + } + credentialsSet := m["es_credentials"].(*schema.Set).List() + if len(credentialsSet) != 0 { + credentialScheme := make(map[string]interface{}) + credentialScheme = credentialsSet[0].(map[string]interface{}) + integrationLogConfig.Elk.EsCredentials = &EsCredentials{ + Username: credentialScheme["username"].(string), + Password: credentialScheme["password"].(string), + } + } + case SplunkKey: + integrationLogConfig.Splunk = &SplunkConfig{ + Hostname: m["hostname"].(string), + HecPort: m["hec_port"].(string), + AccessToken: m["access_token"].(string), + Index: m["index"].(string), + UseTLS: m["use_tls"].(bool), + } + case SumoLogicKey: + integrationLogConfig.SumoLogic = &SumoLogicConfig{ + Address: m["address"].(string), + } + case FluentbitKey: + integrationLogConfig.FluentBit = &FluentBitConfig{ + Config: m["config"].(string), + SkipValidate: m["skip_validate"].(bool), + } + default: + return fmt.Errorf("unexpected config type [%s]", configType) + } + return nil +} + +type ListIntegrationLogsResponse struct { + Integrations []LoggingIntegration `json:"integrations"` +} + +func (resp *ListIntegrationLogsResponse) WriteToSchema(d *schema.ResourceData) error { + integrations := make([]interface{}, len(resp.Integrations)) + for i, integration := range resp.Integrations { + // write in config scheme + configType, config, err := getLoggingConfig(&integration) + if err != nil { + return err + } + integrations[i] = map[string]interface{}{ + "id": integration.Id, + "name": integration.Name, + "receive_audit_logs": integration.ReceiveAuditLogs, + configType: config, + } + } + if err := d.Set("integrations", integrations); err != nil { + return err + } + + d.SetId(uuid.New().String()) -var allLogIntegrationConfigs = []string{ - CloudWatchKey, - DatadogKey, - ElkKey, - SplunkKey, - SumoLogicKey, - FluentbitKey, + return nil } func getIntegrationLogsSchema() map[string]*schema.Schema { diff --git a/cyral/internal/integration/logging/resource.go b/cyral/internal/integration/logging/resource.go new file mode 100644 index 00000000..a3c8570a --- /dev/null +++ b/cyral/internal/integration/logging/resource.go @@ -0,0 +1,35 @@ +package logging + +import ( + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/resourcetype" +) + +var resourceContextHandler = core.DefaultContextHandler{ + ResourceName: resourceName, + ResourceType: resourcetype.Resource, + SchemaReaderFactory: func() core.SchemaReader { return &LoggingIntegration{} }, + SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &LoggingIntegration{} }, + BaseURLFactory: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/integrations/logging", c.ControlPlane) + }, +} + +func resourceSchema() *schema.Resource { + return &schema.Resource{ + Description: "Manages a logging integration that can be used to push logs from Cyral to the corresponding logging system (E.g.: AWS CloudWatch, Splunk, SumoLogic, etc).", + CreateContext: resourceContextHandler.CreateContext(), + ReadContext: resourceContextHandler.ReadContext(), + UpdateContext: resourceContextHandler.UpdateContext(), + DeleteContext: resourceContextHandler.DeleteContext(), + Schema: getIntegrationLogsSchema(), + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + } +} diff --git a/cyral/internal/integration/logging/resource_cyral_integration_logging.go b/cyral/internal/integration/logging/resource_cyral_integration_logging.go deleted file mode 100644 index d62934d3..00000000 --- a/cyral/internal/integration/logging/resource_cyral_integration_logging.go +++ /dev/null @@ -1,237 +0,0 @@ -package logging - -import ( - "fmt" - "net/http" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - - "github.com/cyralinc/terraform-provider-cyral/cyral/client" - "github.com/cyralinc/terraform-provider-cyral/cyral/core" - "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/operationtype" -) - -const loggingApiUrl = "https://%s/v1/integrations/logging/%s" - -func getLoggingConfig(resource *LoggingIntegration) (string, []interface{}, error) { - var configType string - var configScheme []interface{} - switch { - case resource.CloudWatch != nil: - configType = CloudWatchKey - configScheme = []interface{}{ - map[string]interface{}{ - "region": resource.CloudWatch.Region, - "group": resource.CloudWatch.Group, - "stream": resource.CloudWatch.Stream, - }, - } - case resource.Datadog != nil: - configType = DatadogKey - configScheme = []interface{}{ - map[string]interface{}{ - "api_key": resource.Datadog.ApiKey, - }, - } - case resource.Elk != nil: - configType = ElkKey - elkConfig := map[string]interface{}{ - "es_url": resource.Elk.EsURL, - "kibana_url": resource.Elk.KibanaURL, - } - // Optional, so we need to verify separately - if resource.Elk.EsCredentials != nil { - elkConfig["es_credentials"] = []interface{}{ - map[string]interface{}{ - "username": resource.Elk.EsCredentials.Username, - "password": resource.Elk.EsCredentials.Password, - }, - } - } - configScheme = []interface{}{elkConfig} - case resource.Splunk != nil: - configType = SplunkKey - configScheme = []interface{}{ - map[string]interface{}{ - "hostname": resource.Splunk.Hostname, - "hec_port": resource.Splunk.HecPort, - "access_token": resource.Splunk.AccessToken, - "index": resource.Splunk.Index, - "use_tls": resource.Splunk.UseTLS, - }, - } - case resource.SumoLogic != nil: - configType = SumoLogicKey - configScheme = []interface{}{ - map[string]interface{}{ - "address": resource.SumoLogic.Address, - }, - } - case resource.FluentBit != nil: - configType = FluentbitKey - configScheme = []interface{}{ - map[string]interface{}{ - "config": resource.FluentBit.Config, - "skip_validate": resource.FluentBit.SkipValidate, - }, - } - default: - return configType, nil, fmt.Errorf("config scheme is required, log integration config is corrupt: %v", resource) - } - - return configType, configScheme, nil -} - -func (resource *LoggingIntegration) WriteToSchema(d *schema.ResourceData) error { - if err := d.Set("name", resource.Name); err != nil { - return fmt.Errorf("error setting 'name': %w", err) - } - if err := d.Set("receive_audit_logs", resource.ReceiveAuditLogs); err != nil { - return fmt.Errorf("error setting 'receive_audit_logs': %w", err) - } - - configType, configScheme, err := getLoggingConfig(resource) - if err != nil { - return err - } - - if err := d.Set(configType, configScheme); err != nil { - return fmt.Errorf("error setting 'config': %w", err) - } - - return nil -} - -// ReadFromSchema is used to parse the resource schema into a logging integration structure that is expected by the API -func (integrationLogConfig *LoggingIntegration) ReadFromSchema(d *schema.ResourceData) error { - integrationLogConfig.Id = d.Id() //Get("integration_id").(string) - integrationLogConfig.Name = d.Get("name").(string) - integrationLogConfig.ReceiveAuditLogs = d.Get("receive_audit_logs").(bool) - - // Handle Config Scheme (required field). - var configType string - var config interface{} - for _, integrationType := range allLogIntegrationConfigs { - configAux, isConfigOk := d.GetOk(integrationType) - if isConfigOk { - config = configAux.(interface{}) - configType = integrationType - break - } - } - - configDetails := config.(*schema.Set).List() - - m := configDetails[0].(map[string]interface{}) - - switch configType { - case CloudWatchKey: - integrationLogConfig.CloudWatch = &CloudWatchConfig{ - Region: m["region"].(string), - Group: m["group"].(string), - Stream: m["stream"].(string), - } - case DatadogKey: - integrationLogConfig.Datadog = &DataDogConfig{ - ApiKey: m["api_key"].(string), - } - case ElkKey: - integrationLogConfig.Elk = &ElkConfig{ - EsURL: m["es_url"].(string), - KibanaURL: m["kibana_url"].(string), - } - credentialsSet := m["es_credentials"].(*schema.Set).List() - if len(credentialsSet) != 0 { - credentialScheme := make(map[string]interface{}) - credentialScheme = credentialsSet[0].(map[string]interface{}) - integrationLogConfig.Elk.EsCredentials = &EsCredentials{ - Username: credentialScheme["username"].(string), - Password: credentialScheme["password"].(string), - } - } - case SplunkKey: - integrationLogConfig.Splunk = &SplunkConfig{ - Hostname: m["hostname"].(string), - HecPort: m["hec_port"].(string), - AccessToken: m["access_token"].(string), - Index: m["index"].(string), - UseTLS: m["use_tls"].(bool), - } - case SumoLogicKey: - integrationLogConfig.SumoLogic = &SumoLogicConfig{ - Address: m["address"].(string), - } - case FluentbitKey: - integrationLogConfig.FluentBit = &FluentBitConfig{ - Config: m["config"].(string), - SkipValidate: m["skip_validate"].(bool), - } - default: - return fmt.Errorf("unexpected config type [%s]", configType) - } - return nil -} - -func CreateLoggingIntegration() core.ResourceOperationConfig { - return core.ResourceOperationConfig{ - ResourceName: "LoggingIntegrationCreate", - Type: operationtype.Create, - HttpMethod: http.MethodPost, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf("https://%s/v1/integrations/logging", c.ControlPlane) - }, - SchemaReaderFactory: func() core.SchemaReader { return &LoggingIntegration{} }, - SchemaWriterFactory: core.DefaultSchemaWriterFactory, - } -} - -var ReadLoggingIntegration = core.ResourceOperationConfig{ - ResourceName: "LoggingIntegrationRead", - Type: operationtype.Read, - HttpMethod: http.MethodGet, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf(loggingApiUrl, c.ControlPlane, d.Id()) - }, - SchemaWriterFactory: func(_ *schema.ResourceData) core.SchemaWriter { return &LoggingIntegration{} }, - RequestErrorHandler: &core.IgnoreHttpNotFound{ResName: "Integration logging"}, -} - -func UpdateLoggingIntegration() core.ResourceOperationConfig { - return core.ResourceOperationConfig{ - ResourceName: "LoggingIntegrationUpdate", - Type: operationtype.Update, - HttpMethod: http.MethodPut, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf(loggingApiUrl, c.ControlPlane, d.Id()) - }, - SchemaReaderFactory: func() core.SchemaReader { return &LoggingIntegration{} }, - } -} - -func DeleteLoggingIntegration() core.ResourceOperationConfig { - return core.ResourceOperationConfig{ - ResourceName: "LoggingIntegrationDelete", - Type: operationtype.Delete, - HttpMethod: http.MethodDelete, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf(loggingApiUrl, c.ControlPlane, d.Id()) - }, - } -} - -func ResourceIntegrationLogging() *schema.Resource { - return &schema.Resource{ - Description: "Manages a logging integration that can be used to push logs from Cyral to the corresponding logging system (E.g.: AWS CloudWatch, Splunk, SumoLogic, etc).", - CreateContext: core.CreateResource( - CreateLoggingIntegration(), - ReadLoggingIntegration, - ), - ReadContext: core.ReadResource(ReadLoggingIntegration), - UpdateContext: core.UpdateResource(UpdateLoggingIntegration(), ReadLoggingIntegration), - DeleteContext: core.DeleteResource(DeleteLoggingIntegration()), - Schema: getIntegrationLogsSchema(), - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - } -} diff --git a/cyral/internal/integration/logging/resource_cyral_integration_logging_test.go b/cyral/internal/integration/logging/resource_test.go similarity index 100% rename from cyral/internal/integration/logging/resource_cyral_integration_logging_test.go rename to cyral/internal/integration/logging/resource_test.go diff --git a/cyral/internal/integration/logging/schema_loader.go b/cyral/internal/integration/logging/schema_loader.go new file mode 100644 index 00000000..084b6963 --- /dev/null +++ b/cyral/internal/integration/logging/schema_loader.go @@ -0,0 +1,29 @@ +package logging + +import "github.com/cyralinc/terraform-provider-cyral/cyral/core" + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "integration.logging" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: dataSourceName, + Type: core.DataSourceSchemaType, + Schema: dataSourceSchema, + }, + { + Name: resourceName, + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/integration/slack/schema_loader.go b/cyral/internal/integration/slack/schema_loader.go index c08b2fcb..79923129 100644 --- a/cyral/internal/integration/slack/schema_loader.go +++ b/cyral/internal/integration/slack/schema_loader.go @@ -6,7 +6,7 @@ type packageSchema struct { } func (p *packageSchema) Name() string { - return "slack" + return "integration.slack" } func (p *packageSchema) Schemas() []*core.SchemaDescriptor { diff --git a/cyral/internal/integration/teams/schema_loader.go b/cyral/internal/integration/teams/schema_loader.go index 196bcaf8..4d82963a 100644 --- a/cyral/internal/integration/teams/schema_loader.go +++ b/cyral/internal/integration/teams/schema_loader.go @@ -6,7 +6,7 @@ type packageSchema struct { } func (p *packageSchema) Name() string { - return "teams" + return "integration.teams" } func (p *packageSchema) Schemas() []*core.SchemaDescriptor { diff --git a/cyral/internal/permission/constants.go b/cyral/internal/permission/constants.go new file mode 100644 index 00000000..497ca453 --- /dev/null +++ b/cyral/internal/permission/constants.go @@ -0,0 +1,10 @@ +package permission + +const ( + dataSourceName = "cyral_permission" +) + +const ( + // Schema keys + PermissionDataSourcePermissionListKey = "permission_list" +) diff --git a/cyral/internal/permission/data_source_cyral_permission.go b/cyral/internal/permission/datasource.go similarity index 78% rename from cyral/internal/permission/data_source_cyral_permission.go rename to cyral/internal/permission/datasource.go index 8f30afec..d9f4a9d1 100644 --- a/cyral/internal/permission/data_source_cyral_permission.go +++ b/cyral/internal/permission/datasource.go @@ -4,7 +4,6 @@ import ( "fmt" "net/http" - "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/cyralinc/terraform-provider-cyral/cyral/client" @@ -13,26 +12,12 @@ import ( "github.com/cyralinc/terraform-provider-cyral/cyral/utils" ) -const ( - // Schema keys - PermissionDataSourcePermissionListKey = "permission_list" -) - -type PermissionDataSourceResponse struct { - // Permissions correspond to Roles in API. - Permissions []Permission `json:"roles"` -} - -func (response *PermissionDataSourceResponse) WriteToSchema(d *schema.ResourceData) error { - d.SetId(uuid.New().String()) - d.Set(PermissionDataSourcePermissionListKey, permissionsToInterfaceList(response.Permissions)) - return nil -} - -func DataSourcePermission() *schema.Resource { +func dataSourceSchema() *schema.Resource { return &schema.Resource{ Description: "Retrieve all Cyral permissions. See also resource " + "[`cyral_service_account`](../resources/service_account.md).", + // The DefaultContextHandler is NOT used here as this data source intentionally + // does not handle 404 errors, returning them to the user. ReadContext: core.ReadResource( core.ResourceOperationConfig{ ResourceName: "PermissionDataSourceRead", diff --git a/cyral/internal/permission/data_source_cyral_permission_test.go b/cyral/internal/permission/datasource_test.go similarity index 100% rename from cyral/internal/permission/data_source_cyral_permission_test.go rename to cyral/internal/permission/datasource_test.go diff --git a/cyral/internal/permission/model_permission.go b/cyral/internal/permission/model.go similarity index 67% rename from cyral/internal/permission/model_permission.go rename to cyral/internal/permission/model.go index 2cc1c1eb..3b57e0d3 100644 --- a/cyral/internal/permission/model_permission.go +++ b/cyral/internal/permission/model.go @@ -2,6 +2,8 @@ package permission import ( "github.com/cyralinc/terraform-provider-cyral/cyral/utils" + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) type Permission struct { @@ -37,3 +39,14 @@ var AllPermissionNames = []string{ "View Users", "Modify Integrations", } + +type PermissionDataSourceResponse struct { + // Permissions correspond to Roles in API. + Permissions []Permission `json:"roles"` +} + +func (response *PermissionDataSourceResponse) WriteToSchema(d *schema.ResourceData) error { + d.SetId(uuid.New().String()) + d.Set(PermissionDataSourcePermissionListKey, permissionsToInterfaceList(response.Permissions)) + return nil +} diff --git a/cyral/internal/permission/schema_loader.go b/cyral/internal/permission/schema_loader.go new file mode 100644 index 00000000..aba139de --- /dev/null +++ b/cyral/internal/permission/schema_loader.go @@ -0,0 +1,26 @@ +package permission + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" +) + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "permission" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: dataSourceName, + Type: core.DataSourceSchemaType, + Schema: dataSourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/policy/constants.go b/cyral/internal/policy/constants.go new file mode 100644 index 00000000..57486e47 --- /dev/null +++ b/cyral/internal/policy/constants.go @@ -0,0 +1,5 @@ +package policy + +const ( + resourceName = "cyral_policy" +) diff --git a/cyral/internal/policy/model.go b/cyral/internal/policy/model.go new file mode 100644 index 00000000..df0cf27a --- /dev/null +++ b/cyral/internal/policy/model.go @@ -0,0 +1,109 @@ +package policy + +import ( + "fmt" + "time" + + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type PolicyMetadata struct { + ID string `json:"id"` + Name string `json:"name"` + Version string `json:"version"` + Created time.Time `json:"created"` + LastUpdated time.Time `json:"lastUpdated"` + Type string `json:"type"` + Tags []string `json:"tags"` + Enabled bool `json:"enabled"` + Description string `json:"description"` +} + +type PolicyListResponse struct { + // Policies is a list of policy identifiers. + Policies []string `json:"Policies,omitempty"` +} + +type Policy struct { + Meta *PolicyMetadata `json:"meta"` + Data []string `json:"data,omitempty"` + Tags []string `json:"tags,omitempty"` +} + +func (r Policy) WriteToSchema(d *schema.ResourceData) error { + if err := d.Set("created", r.Meta.Created.String()); err != nil { + return fmt.Errorf("error setting 'created' field: %w", err) + } + if err := d.Set("data", r.Data); err != nil { + return fmt.Errorf("error setting 'data' field: %w", err) + } + if err := d.Set("data_label_tags", r.Tags); err != nil { + return fmt.Errorf("error setting 'data_label_tags' field: %w", err) + } + if err := d.Set("description", r.Meta.Description); err != nil { + return fmt.Errorf("error setting 'description' field: %w", err) + } + if err := d.Set("enabled", r.Meta.Enabled); err != nil { + return fmt.Errorf("error setting 'enabled' field: %w", err) + } + if err := d.Set("last_updated", r.Meta.LastUpdated.String()); err != nil { + return fmt.Errorf("error setting 'last_updated' field: %w", err) + } + if err := d.Set("name", r.Meta.Name); err != nil { + return fmt.Errorf("error setting 'name' field: %w", err) + } + if err := d.Set("type", r.Meta.Type); err != nil { + return fmt.Errorf("error setting 'type' field: %w", err) + } + if err := d.Set("version", r.Meta.Version); err != nil { + return fmt.Errorf("error setting 'version' field: %w", err) + } + // Once the `tags` field is removed, this conditional logic should also be + // removed and only the `metadata_tags` should be set. + _, isDeprecatedFieldSet := d.GetOk("tags") + if isDeprecatedFieldSet { + if err := d.Set("tags", r.Meta.Tags); err != nil { + return fmt.Errorf("error setting 'tags' field: %w", err) + } + } else { + if err := d.Set("metadata_tags", r.Meta.Tags); err != nil { + return fmt.Errorf("error setting 'metadata_tags' field: %w", err) + } + + } + + return nil +} + +func (r *Policy) ReadFromSchema(d *schema.ResourceData) error { + r.Data = utils.GetStrListFromSchemaField(d, "data") + r.Tags = utils.GetStrListFromSchemaField(d, "data_label_tags") + metadataTags := utils.GetStrListFromSchemaField(d, "metadata_tags") + if len(metadataTags) == 0 { + metadataTags = utils.GetStrListFromSchemaField(d, "tags") + } + r.Meta = &PolicyMetadata{ + Tags: metadataTags, + } + + if v, ok := d.Get("name").(string); ok { + r.Meta.Name = v + } + + if v, ok := d.Get("version").(string); ok { + r.Meta.Version = v + } + + r.Meta.Type = "terraform" + + if v, ok := d.Get("enabled").(bool); ok { + r.Meta.Enabled = v + } + + if v, ok := d.Get("description").(string); ok { + r.Meta.Description = v + } + + return nil +} diff --git a/cyral/internal/policy/model_policy.go b/cyral/internal/policy/model_policy.go deleted file mode 100644 index 500dda8f..00000000 --- a/cyral/internal/policy/model_policy.go +++ /dev/null @@ -1,28 +0,0 @@ -package policy - -import ( - "time" -) - -type Policy struct { - Meta *PolicyMetadata `json:"meta"` - Data []string `json:"data,omitempty"` - Tags []string `json:"tags,omitempty"` -} - -type PolicyMetadata struct { - ID string `json:"id"` - Name string `json:"name"` - Version string `json:"version"` - Created time.Time `json:"created"` - LastUpdated time.Time `json:"lastUpdated"` - Type string `json:"type"` - Tags []string `json:"tags"` - Enabled bool `json:"enabled"` - Description string `json:"description"` -} - -type PolicyListResponse struct { - // Policies is a list of policy identifiers. - Policies []string `json:"Policies,omitempty"` -} diff --git a/cyral/internal/policy/resource_cyral_policy.go b/cyral/internal/policy/resource.go similarity index 52% rename from cyral/internal/policy/resource_cyral_policy.go rename to cyral/internal/policy/resource.go index dd39ab60..19421d9d 100644 --- a/cyral/internal/policy/resource_cyral_policy.go +++ b/cyral/internal/policy/resource.go @@ -8,21 +8,44 @@ import ( "github.com/cyralinc/terraform-provider-cyral/cyral/client" "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/operationtype" + "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/resourcetype" "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -func ResourcePolicy() *schema.Resource { +var resourceContextHandler = core.DefaultContextHandler{ + ResourceName: resourceName, + ResourceType: resourcetype.Resource, + SchemaReaderFactory: func() core.SchemaReader { return &Policy{} }, + SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &Policy{} }, + BaseURLFactory: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/policies", c.ControlPlane) + }, +} + +func resourceSchema() *schema.Resource { return &schema.Resource{ Description: "Manages [policies](https://cyral.com/docs/reference/policy). See also: " + "[Policy Rule](./policy_rule.md). For more information, see the " + "[Policy Guide](https://cyral.com/docs/policy/overview).", - CreateContext: resourcePolicyCreate, - ReadContext: resourcePolicyRead, - UpdateContext: resourcePolicyUpdate, - DeleteContext: resourcePolicyDelete, + CreateContext: resourceContextHandler.CreateContext(), + ReadContext: resourceContextHandler.ReadContextCustomErrorHandling(&core.IgnoreNotFoundByMessage{ + ResName: resourceName, + MessageMatches: "policy not found", + OperationType: operationtype.Read, + }), + UpdateContext: resourceContextHandler.UpdateContextCustomErrorHandling(&core.IgnoreNotFoundByMessage{ + ResName: resourceName, + MessageMatches: "policy not found", + OperationType: operationtype.Update, + }, nil), + DeleteContext: resourceContextHandler.DeleteContextCustomErrorHandling(&core.IgnoreNotFoundByMessage{ + ResName: resourceName, + MessageMatches: "policy not found", + OperationType: operationtype.Delete, + }), Schema: map[string]*schema.Schema{ "created": { Description: "Timestamp for the policy creation.", @@ -116,145 +139,6 @@ func ResourcePolicy() *schema.Resource { } } -func resourcePolicyCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Debug(ctx, "Init resourcePolicyCreate") - c := m.(*client.Client) - - d.Set("type", "terraform") - policy := getPolicyInfoFromResource(d) - - url := fmt.Sprintf("https://%s/v1/policies", c.ControlPlane) - - body, err := c.DoRequest(ctx, url, http.MethodPost, policy) - if err != nil { - return utils.CreateError("Unable to create policy", fmt.Sprintf("%v", err)) - } - - response := core.IDBasedResponse{} - if err := json.Unmarshal(body, &response); err != nil { - return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) - } - tflog.Debug(ctx, fmt.Sprintf("Response body (unmarshalled): %#v", response)) - - d.SetId(response.ID) - - tflog.Debug(ctx, "End resourcePolicyCreate") - - return resourcePolicyRead(ctx, d, m) -} - -func resourcePolicyRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Debug(ctx, "Init resourcePolicyRead") - c := m.(*client.Client) - - url := fmt.Sprintf("https://%s/v1/policies/%s", c.ControlPlane, d.Id()) - - body, err := c.DoRequest(ctx, url, http.MethodGet, nil) - if err != nil { - return utils.CreateError("Unable to read policy", fmt.Sprintf("%v", err)) - } - - response := Policy{} - if err := json.Unmarshal(body, &response); err != nil { - return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) - } - tflog.Debug(ctx, fmt.Sprintf("Response body (unmarshalled): %#v", response)) - - d.Set("created", response.Meta.Created.String()) - d.Set("data", response.Data) - d.Set("data_label_tags", response.Tags) - d.Set("description", response.Meta.Description) - d.Set("enabled", response.Meta.Enabled) - d.Set("last_updated", response.Meta.LastUpdated.String()) - d.Set("name", response.Meta.Name) - d.Set("type", response.Meta.Type) - d.Set("version", response.Meta.Version) - // Once the `tags` field is removed, this conditional logic should also be - // removed and only the `metadata_tags` should be set. - _, isDeprecatedFieldSet := d.GetOk("tags") - if isDeprecatedFieldSet { - d.Set("tags", response.Meta.Tags) - } else { - d.Set("metadata_tags", response.Meta.Tags) - } - - tflog.Debug(ctx, "End resourcePolicyRead") - return diag.Diagnostics{} -} - -func resourcePolicyUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Debug(ctx, "Init resourcePolicyUpdate") - c := m.(*client.Client) - - d.Set("type", "terraform") - policy := getPolicyInfoFromResource(d) - - url := fmt.Sprintf("https://%s/v1/policies/%s", c.ControlPlane, d.Id()) - - _, err := c.DoRequest(ctx, url, http.MethodPut, policy) - if err != nil { - return utils.CreateError("Unable to update policy", fmt.Sprintf("%v", err)) - } - - tflog.Debug(ctx, "End resourcePolicyUpdate") - - return resourcePolicyRead(ctx, d, m) -} - -func resourcePolicyDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Debug(ctx, "Init resourcePolicyDelete") - c := m.(*client.Client) - - url := fmt.Sprintf("https://%s/v1/policies/%s", c.ControlPlane, d.Id()) - - if _, err := c.DoRequest(ctx, url, http.MethodDelete, nil); err != nil { - return utils.CreateError("Unable to delete policy", fmt.Sprintf("%v", err)) - } - - tflog.Debug(ctx, "End resourcePolicyDelete") - - return diag.Diagnostics{} -} - -func getPolicyInfoFromResource(d *schema.ResourceData) Policy { - data := utils.GetStrListFromSchemaField(d, "data") - dataTags := utils.GetStrListFromSchemaField(d, "data_label_tags") - metadataTags := utils.GetStrListFromSchemaField(d, "metadata_tags") - if len(metadataTags) == 0 { - metadataTags = utils.GetStrListFromSchemaField(d, "tags") - } - - policy := Policy{ - Data: data, - Tags: dataTags, - Meta: &PolicyMetadata{ - Tags: metadataTags, - }, - } - - if v, ok := d.Get("name").(string); ok { - policy.Meta.Name = v - } - - if v, ok := d.Get("version").(string); ok { - policy.Meta.Version = v - } - - if v, ok := d.Get("type").(string); ok { - policy.Meta.Type = v - } - - if v, ok := d.Get("enabled").(bool); ok { - policy.Meta.Enabled = v - } - - if v, ok := d.Get("description").(string); ok { - policy.Meta.Description = v - } - - return policy -} - func ListPolicies(c *client.Client) ([]Policy, error) { ctx := context.Background() tflog.Debug(ctx, "Init ListPolicies") diff --git a/cyral/internal/policy/resource_cyral_policy_test.go b/cyral/internal/policy/resource_test.go similarity index 100% rename from cyral/internal/policy/resource_cyral_policy_test.go rename to cyral/internal/policy/resource_test.go diff --git a/cyral/internal/policy/rule/constants.go b/cyral/internal/policy/rule/constants.go new file mode 100644 index 00000000..6a45671d --- /dev/null +++ b/cyral/internal/policy/rule/constants.go @@ -0,0 +1,5 @@ +package rule + +const ( + resourceName = "cyral_policy_rule" +) diff --git a/cyral/internal/policy/rule/model.go b/cyral/internal/policy/rule/model.go new file mode 100644 index 00000000..3e8f1812 --- /dev/null +++ b/cyral/internal/policy/rule/model.go @@ -0,0 +1,253 @@ +package rule + +import ( + "context" + "fmt" + + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type PolicyRule struct { + Deletes []Rule `json:"deletes,omitempty"` + Hosts []string `json:"hosts,omitempty"` + Identities *Identity `json:"identities,omitempty"` + Reads []Rule `json:"reads,omitempty"` + RuleID string `json:"ruleId"` + Updates []Rule `json:"updates,omitempty"` +} + +type Rule struct { + AdditionalChecks string `json:"additionalChecks"` + Data []string `json:"data,omitempty"` + DatasetRewrites []DatasetRewrite `json:"datasetRewrites,omitempty"` + Rows int `json:"rows"` + Severity string `json:"severity"` + RateLimit int `json:"rateLimit"` +} + +type DatasetRewrite struct { + Dataset string `json:"dataset"` + Parameters []string `json:"parameters,omitempty"` + Repo string `json:"repo"` + Substitution string `json:"substitution"` +} + +type Identity struct { + DBRoles []string `json:"dbRoles,omitempty"` + Groups []string `json:"groups,omitempty"` + Services []string `json:"services,omitempty"` + Users []string `json:"users,omitempty"` +} + +func (r PolicyRule) WriteToSchema(d *schema.ResourceData) error { + ctx := context.Background() + deletes := flattenRulesList(ctx, r.Deletes) + tflog.Debug(ctx, fmt.Sprintf("flattened deletes %#v", deletes)) + if err := d.Set("deletes", deletes); err != nil { + return fmt.Errorf("error setting 'deletes' field: %w", err) + } + + reads := flattenRulesList(ctx, r.Reads) + tflog.Debug(ctx, fmt.Sprintf("flattened reads %#v", reads)) + if err := d.Set("reads", reads); err != nil { + return fmt.Errorf("error setting 'reads' field: %w", err) + } + + updates := flattenRulesList(ctx, r.Updates) + tflog.Debug(ctx, fmt.Sprintf("flattened updates %#v", updates)) + if err := d.Set("updates", updates); err != nil { + return fmt.Errorf("error setting 'updates' field: %w", err) + } + + if r.Identities != nil { + if r.Identities.DBRoles != nil || r.Identities.Users != nil || + r.Identities.Groups != nil || r.Identities.Services != nil { + identities := flattenIdentities(ctx, r.Identities) + tflog.Debug(ctx, fmt.Sprintf("flattened identities %#v", identities)) + if err := d.Set("identities", identities); err != nil { + return fmt.Errorf("error setting 'identities' field: %w", err) + } + } + } + + d.Set("hosts", r.Hosts) + + _, policyRuleID := unmarshalPolicyRuleID(d) + // Computed arguments + d.Set("policy_rule_id", policyRuleID) + + return nil +} + +func (r *PolicyRule) ReadFromSchema(d *schema.ResourceData) error { + hosts := utils.GetStrListFromSchemaField(d, "hosts") + + identity := d.Get("identities").([]interface{}) + + ctx := context.Background() + + var identities *Identity + for _, id := range identity { + idMap := id.(map[string]interface{}) + + identities = &Identity{ + DBRoles: getStrListFromInterfaceList(ctx, idMap["db_roles"].([]interface{})), + Groups: getStrListFromInterfaceList(ctx, idMap["groups"].([]interface{})), + Services: getStrListFromInterfaceList(ctx, idMap["services"].([]interface{})), + Users: getStrListFromInterfaceList(ctx, idMap["users"].([]interface{})), + } + } + + r.Deletes = getRuleListFromResource(ctx, d, "deletes") + r.Hosts = hosts + r.Identities = identities + r.Reads = getRuleListFromResource(ctx, d, "reads") + r.Updates = getRuleListFromResource(ctx, d, "updates") + + return nil +} + +func getRuleListFromResource(ctx context.Context, d *schema.ResourceData, name string) []Rule { + tflog.Trace(ctx, "Init getRuleListFromResource") + ruleInfoList := d.Get(name).([]interface{}) + ruleList := make([]Rule, 0, len(ruleInfoList)) + + for _, ruleInterface := range ruleInfoList { + ruleMap := ruleInterface.(map[string]interface{}) + + rule := Rule{ + AdditionalChecks: ruleMap["additional_checks"].(string), + Data: getStrListFromInterfaceList(ctx, ruleMap["data"].([]interface{})), + DatasetRewrites: getDatasetRewrites(ctx, ruleMap["dataset_rewrites"].([]interface{})), + Rows: ruleMap["rows"].(int), + Severity: ruleMap["severity"].(string), + RateLimit: ruleMap["rate_limit"].(int), + } + + ruleList = append(ruleList, rule) + } + tflog.Trace(ctx, "End getRuleListFromResource") + + return ruleList +} + +func unmarshalPolicyRuleID(d *schema.ResourceData) (policyID string, policyRuleID string) { + // We must be careful when dealing with the ID. Specially in the Read + // operation, due to state upgrade from v0 of this resource's schema to + // v1. In v0, there exists only one field (the policy rule + // ID). Therefore, if we assume there are two, the first `terraform + // refresh` done when upgrading will fail. + ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) + if err == nil { + // This is the new way to organize the IDs (v1). + policyID = ids[0] + policyRuleID = ids[1] + } else { + // This conditional branch is here to treat legacy resources (v0). + policyID = d.Get("policy_id").(string) + policyRuleID = d.Id() + } + return +} + +func getStrListFromInterfaceList(ctx context.Context, interfaceList []interface{}) []string { + tflog.Trace(ctx, "Init getStrListFromInterfaceList") + + strList := []string{} + + for _, i := range interfaceList { + strList = append(strList, i.(string)) + } + + tflog.Trace(ctx, "End getStrListFromInterfaceList") + + return strList +} + +func getDatasetRewrites(ctx context.Context, datasetList []interface{}) []DatasetRewrite { + tflog.Trace(ctx, "Init getDatasetRewrites") + + datasetRewrites := make([]DatasetRewrite, 0, len(datasetList)) + + for _, d := range datasetList { + datasetMap := d.(map[string]interface{}) + + datasetRewrite := DatasetRewrite{ + Dataset: datasetMap["dataset"].(string), + Repo: datasetMap["repo"].(string), + Substitution: datasetMap["substitution"].(string), + Parameters: getStrListFromInterfaceList(ctx, datasetMap["parameters"].([]interface{})), + } + + datasetRewrites = append(datasetRewrites, datasetRewrite) + } + + tflog.Trace(ctx, "End getDatasetRewrites") + + return datasetRewrites +} + +func flattenIdentities(ctx context.Context, identities *Identity) []interface{} { + tflog.Trace(ctx, "Init flattenIdentities") + tflog.Trace(ctx, fmt.Sprintf("identities %#v", identities)) + identityMap := make(map[string]interface{}) + + identityMap["db_roles"] = identities.DBRoles + identityMap["groups"] = identities.Groups + identityMap["services"] = identities.Services + identityMap["users"] = identities.Users + + tflog.Trace(ctx, "End flattenIdentities") + return []interface{}{identityMap} +} + +func flattenRulesList(ctx context.Context, rulesList []Rule) []interface{} { + tflog.Trace(ctx, "Init flattenRulesList") + if rulesList != nil { + rules := make([]interface{}, len(rulesList), len(rulesList)) + + for i, rule := range rulesList { + ruleMap := make(map[string]interface{}) + + datasetRewriteList := make([]interface{}, len(rule.DatasetRewrites), len(rule.DatasetRewrites)) + + for j, datasetRewrite := range rule.DatasetRewrites { + drMap := make(map[string]interface{}) + drMap["dataset"] = datasetRewrite.Dataset + drMap["repo"] = datasetRewrite.Repo + drMap["substitution"] = datasetRewrite.Substitution + drMap["parameters"] = datasetRewrite.Parameters + + datasetRewriteList[j] = drMap + } + + ruleMap["additional_checks"] = rule.AdditionalChecks + ruleMap["data"] = rule.Data + ruleMap["dataset_rewrites"] = datasetRewriteList + ruleMap["rows"] = rule.Rows + ruleMap["severity"] = rule.Severity + ruleMap["rate_limit"] = rule.RateLimit + + rules[i] = ruleMap + } + + return rules + } + tflog.Trace(ctx, "End flattenRulesList") + + return make([]interface{}, 0) +} + +type PolicyRuleIDBasedResponse struct { + ID string `json:"id"` +} + +func (r PolicyRuleIDBasedResponse) WriteToSchema(d *schema.ResourceData) error { + d.SetId(utils.MarshalComposedID([]string{ + d.Get("policy_id").(string), + r.ID}, + "/")) + return nil +} diff --git a/cyral/internal/policy/rule/resource.go b/cyral/internal/policy/rule/resource.go new file mode 100644 index 00000000..a9880b97 --- /dev/null +++ b/cyral/internal/policy/rule/resource.go @@ -0,0 +1,277 @@ +package rule + +import ( + "context" + "fmt" + "strings" + + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/operationtype" + "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/resourcetype" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var resourceContextHandler = core.DefaultContextHandler{ + ResourceName: resourceName, + ResourceType: resourcetype.Resource, + SchemaReaderFactory: func() core.SchemaReader { return &PolicyRule{} }, + SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &PolicyRule{} }, + SchemaWriterFactoryPostMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &PolicyRuleIDBasedResponse{} }, + BaseURLFactory: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/policies/%s/rules", c.ControlPlane, d.Get("policy_id").(string)) + }, + ReadUpdateDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { + policyID, policyRuleID := unmarshalPolicyRuleID(d) + return fmt.Sprintf("https://%s/v1/policies/%s/rules/%s", + c.ControlPlane, policyID, policyRuleID) + }, +} + +func resourceSchema() *schema.Resource { + return &schema.Resource{ + Description: "Manages [policy rules](https://cyral.com/docs/reference/policy/#rules). " + + "See also the [`cyral_policy`](https://registry.terraform.io/providers/cyralinc/cyral/latest/docs/resources/policy) " + + "resource and the [Policy Guide](https://cyral.com/docs/policy#the-rules-block-of-a-policy)." + + "\n\n-> 1. Unless you create a default rule, users and groups only have the rights you explicitly grant them." + + "
2. Each contexted rule comprises these fields: `data`, `rows`, `severity` `additional_checks`, `dataset_rewrites`. The only required fields are `data` and `rows`." + + "
3. The rules block does not need to include all three operation types (reads, updates and deletes); actions you omit are disallowed." + + "
4. If you do not include a hosts block, Cyral does not enforce limits based on the connecting client's host address.", + + CreateContext: resourceContextHandler.CreateContext(), + ReadContext: resourceContextHandler.ReadContextCustomErrorHandling(&core.IgnoreNotFoundByMessage{ + ResName: resourceName, + MessageMatches: "not found in policy", + OperationType: operationtype.Read, + }), + UpdateContext: resourceContextHandler.UpdateContextCustomErrorHandling(&core.IgnoreNotFoundByMessage{ + ResName: resourceName, + MessageMatches: "not found in policy", + OperationType: operationtype.Update, + }, nil), + DeleteContext: resourceContextHandler.DeleteContextCustomErrorHandling(&core.IgnoreNotFoundByMessage{ + ResName: resourceName, + MessageMatches: "not found in policy", + OperationType: operationtype.Delete, + }), + + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Version: 0, + Type: policyRuleResourceSchemaV0(). + CoreConfigSchema().ImpliedType(), + Upgrade: UpgradePolicyRuleV0, + }, + }, + + Schema: policyRuleResourceSchemaV0().Schema, + + Importer: &schema.ResourceImporter{ + StateContext: func( + ctx context.Context, + d *schema.ResourceData, + m interface{}, + ) ([]*schema.ResourceData, error) { + ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) + if err != nil { + return nil, err + } + policyID := ids[0] + d.Set("policy_id", policyID) + return []*schema.ResourceData{d}, nil + }, + }, + } +} + +func ruleSchema(description string) *schema.Schema { + return &schema.Schema{ + Description: description, + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "additional_checks": { + Description: "Constraints on the data access specified in " + + "[Rego](https://www.openpolicyagent.org/docs/latest/policy-language/). " + + "See [Additional checks](https://cyral.com/docs/policy/rules/#additional-checks).", + Type: schema.TypeString, + Optional: true, + }, + "data": { + Type: schema.TypeList, + Description: "The data locations protected by this rule. " + + "Use `*` if you want to define `any` data location. " + + "For more information, see the " + + "[policy rules](https://cyral.com/docs/policy/rules#contexted-rules) documentation.", + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "dataset_rewrites": { + Description: "Defines how requests should be rewritten in the case of " + + "policy violations. See [Request rewriting](https://cyral.com/docs/policy/rules/#request-rewriting).", + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "dataset": { + Description: "The dataset that should be rewritten." + + "In the case of Snowflake, this denotes a fully qualified table name in the form: " + + "`..`", + Type: schema.TypeString, + Required: true, + }, + "repo": { + Description: "The name of the repository that the rewrite applies to.", + Type: schema.TypeString, + Required: true, + }, + "substitution": { + Description: "The request used to substitute references to the dataset.", + Type: schema.TypeString, + Required: true, + }, + "parameters": { + Description: "The set of parameters used in the substitution request, " + + "these are references to fields in the activity log as described in " + + "the [Additional Checks section](https://cyral.com/docs/policy/rules/#additional-checks).", + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "rows": { + Description: "The number of records (for example, rows or documents) that can " + + "be accessed/affected in a single statement. Use positive integer " + + "numbers to define how many records. If you want to define `any` " + + "number of records, set to `-1`.", + Type: schema.TypeInt, + Required: true, + }, + "severity": { + Description: "severity level that's recorded when someone violate this rule. " + + "This is an informational value. Settings: (`low` | `medium` | `high`). " + + "If not specified, the severity is considered to be low.", + Type: schema.TypeString, + Optional: true, + Default: "low", + }, + "rate_limit": { + Description: "Rate Limit specifies the limit of calls that a user can make within a given time period.", + Type: schema.TypeInt, + Optional: true, + }, + }, + }, + } +} + +func policyRuleResourceSchemaV0() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "policy_id": { + Description: "The ID of the policy you are adding this rule to.", + Type: schema.TypeString, + Required: true, + }, + "deletes": ruleSchema("A contexted rule for accesses of the type `delete`."), + "reads": ruleSchema("A contexted rule for accesses of the type `read`."), + "updates": ruleSchema("A contexted rule for accesses of the type `update`."), + "hosts": { + Description: "Hosts specification that limits access to only those users connecting from a certain network location.", + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "identities": { + Description: "Identities specifies the people, applications, " + + "or groups this rule applies to. Every rule except your default rule has one. " + + "It can have 4 fields: `db_roles`, `groups`, `users` and `services`.", + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "db_roles": { + Description: "Database roles that this rule will apply to.", + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "groups": { + Description: "Groups that this rule will apply to.", + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "services": { + Description: "Services that this rule will apply to.", + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "users": { + Description: "Users that this rule will apply to.", + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + + // Computed arguments + "policy_rule_id": { + Description: "The ID of the policy rule.", + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +// Previously, the ID of `cyral_policy_rule` was simply the policy rule ID. That +// is not ideal, because to realize the resource we also need the policy ID +// itself. That creates an inconsistency between the ID syntax used in +// `terraform import` and the computed id for the resource. The goal of this +// upgrade is to set the `id` attribute to have the format +// `{policy_id}/{policy_rule_id}`. +func UpgradePolicyRuleV0( + _ context.Context, + rawState map[string]interface{}, + _ interface{}, +) (map[string]interface{}, error) { + policyRuleID := rawState["id"].(string) + + // Make sure there is not `/` in the previous ID, otherwise the new + // resource state ID will become inconsistent. + idComponents := strings.Split(policyRuleID, "/") + if len(idComponents) > 1 { + return rawState, fmt.Errorf("unexpected format for resource ID: " + + `found more than one field separated by "/"`) + } + + policyID := rawState["policy_id"].(string) + newID := utils.MarshalComposedID([]string{policyID, policyRuleID}, "/") + rawState["id"] = newID + + return rawState, nil +} diff --git a/cyral/internal/policy/rule/resource_cyral_policy_rule.go b/cyral/internal/policy/rule/resource_cyral_policy_rule.go deleted file mode 100644 index 4b0e0f80..00000000 --- a/cyral/internal/policy/rule/resource_cyral_policy_rule.go +++ /dev/null @@ -1,568 +0,0 @@ -package rule - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "strings" - - "github.com/cyralinc/terraform-provider-cyral/cyral/client" - "github.com/cyralinc/terraform-provider-cyral/cyral/core" - "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/operationtype" - "github.com/cyralinc/terraform-provider-cyral/cyral/utils" - "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -type PolicyRule struct { - Deletes []Rule `json:"deletes,omitempty"` - Hosts []string `json:"hosts,omitempty"` - Identities *Identity `json:"identities,omitempty"` - Reads []Rule `json:"reads,omitempty"` - RuleID string `json:"ruleId"` - Updates []Rule `json:"updates,omitempty"` -} - -type Rule struct { - AdditionalChecks string `json:"additionalChecks"` - Data []string `json:"data,omitempty"` - DatasetRewrites []DatasetRewrite `json:"datasetRewrites,omitempty"` - Rows int `json:"rows"` - Severity string `json:"severity"` - RateLimit int `json:"rateLimit"` -} - -type DatasetRewrite struct { - Dataset string `json:"dataset"` - Parameters []string `json:"parameters,omitempty"` - Repo string `json:"repo"` - Substitution string `json:"substitution"` -} - -type Identity struct { - DBRoles []string `json:"dbRoles,omitempty"` - Groups []string `json:"groups,omitempty"` - Services []string `json:"services,omitempty"` - Users []string `json:"users,omitempty"` -} - -func unmarshalPolicyRuleID(d *schema.ResourceData) (policyID string, policyRuleID string) { - // We must be careful when dealing with the ID. Specially in the Read - // operation, due to state upgrade from v0 of this resource's schema to - // v1. In v0, there exists only one field (the policy rule - // ID). Therefore, if we assume there are two, the first `terraform - // refresh` done when upgrading will fail. - ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) - if err == nil { - // This is the new way to organize the IDs (v1). - policyID = ids[0] - policyRuleID = ids[1] - } else { - // This conditional branch is here to treat legacy resources (v0). - policyID = d.Get("policy_id").(string) - policyRuleID = d.Id() - } - return -} - -func ruleSchema(description string) *schema.Schema { - return &schema.Schema{ - Description: description, - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "additional_checks": { - Description: "Constraints on the data access specified in " + - "[Rego](https://www.openpolicyagent.org/docs/latest/policy-language/). " + - "See [Additional checks](https://cyral.com/docs/policy/rules/#additional-checks).", - Type: schema.TypeString, - Optional: true, - }, - "data": { - Type: schema.TypeList, - Description: "The data locations protected by this rule. " + - "Use `*` if you want to define `any` data location. " + - "For more information, see the " + - "[policy rules](https://cyral.com/docs/policy/rules#contexted-rules) documentation.", - Required: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "dataset_rewrites": { - Description: "Defines how requests should be rewritten in the case of " + - "policy violations. See [Request rewriting](https://cyral.com/docs/policy/rules/#request-rewriting).", - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "dataset": { - Description: "The dataset that should be rewritten." + - "In the case of Snowflake, this denotes a fully qualified table name in the form: " + - "`..
`", - Type: schema.TypeString, - Required: true, - }, - "repo": { - Description: "The name of the repository that the rewrite applies to.", - Type: schema.TypeString, - Required: true, - }, - "substitution": { - Description: "The request used to substitute references to the dataset.", - Type: schema.TypeString, - Required: true, - }, - "parameters": { - Description: "The set of parameters used in the substitution request, " + - "these are references to fields in the activity log as described in " + - "the [Additional Checks section](https://cyral.com/docs/policy/rules/#additional-checks).", - Type: schema.TypeList, - Required: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - }, - }, - }, - "rows": { - Description: "The number of records (for example, rows or documents) that can " + - "be accessed/affected in a single statement. Use positive integer " + - "numbers to define how many records. If you want to define `any` " + - "number of records, set to `-1`.", - Type: schema.TypeInt, - Required: true, - }, - "severity": { - Description: "severity level that's recorded when someone violate this rule. " + - "This is an informational value. Settings: (`low` | `medium` | `high`). " + - "If not specified, the severity is considered to be low.", - Type: schema.TypeString, - Optional: true, - Default: "low", - }, - "rate_limit": { - Description: "Rate Limit specifies the limit of calls that a user can make within a given time period.", - Type: schema.TypeInt, - Optional: true, - }, - }, - }, - } -} - -func policyRuleResourceSchemaV0() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - "policy_id": { - Description: "The ID of the policy you are adding this rule to.", - Type: schema.TypeString, - Required: true, - }, - "deletes": ruleSchema("A contexted rule for accesses of the type `delete`."), - "reads": ruleSchema("A contexted rule for accesses of the type `read`."), - "updates": ruleSchema("A contexted rule for accesses of the type `update`."), - "hosts": { - Description: "Hosts specification that limits access to only those users connecting from a certain network location.", - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "identities": { - Description: "Identities specifies the people, applications, " + - "or groups this rule applies to. Every rule except your default rule has one. " + - "It can have 4 fields: `db_roles`, `groups`, `users` and `services`.", - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "db_roles": { - Description: "Database roles that this rule will apply to.", - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "groups": { - Description: "Groups that this rule will apply to.", - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "services": { - Description: "Services that this rule will apply to.", - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "users": { - Description: "Users that this rule will apply to.", - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - }, - }, - }, - - // Computed arguments - "policy_rule_id": { - Description: "The ID of the policy rule.", - Type: schema.TypeString, - Computed: true, - }, - }, - } -} - -// Previously, the ID of `cyral_policy_rule` was simply the policy rule ID. That -// is not ideal, because to realize the resource we also need the policy ID -// itself. That creates an inconsistency between the ID syntax used in -// `terraform import` and the computed id for the resource. The goal of this -// upgrade is to set the `id` attribute to have the format -// `{policy_id}/{policy_rule_id}`. -func UpgradePolicyRuleV0( - _ context.Context, - rawState map[string]interface{}, - _ interface{}, -) (map[string]interface{}, error) { - policyRuleID := rawState["id"].(string) - - // Make sure there is not `/` in the previous ID, otherwise the new - // resource state ID will become inconsistent. - idComponents := strings.Split(policyRuleID, "/") - if len(idComponents) > 1 { - return rawState, fmt.Errorf("unexpected format for resource ID: " + - `found more than one field separated by "/"`) - } - - policyID := rawState["policy_id"].(string) - newID := utils.MarshalComposedID([]string{policyID, policyRuleID}, "/") - rawState["id"] = newID - - return rawState, nil -} - -func ResourcePolicyRule() *schema.Resource { - return &schema.Resource{ - Description: "Manages [policy rules](https://cyral.com/docs/reference/policy/#rules). " + - "See also the [`cyral_policy`](https://registry.terraform.io/providers/cyralinc/cyral/latest/docs/resources/policy) " + - "resource and the [Policy Guide](https://cyral.com/docs/policy#the-rules-block-of-a-policy)." + - "\n\n-> 1. Unless you create a default rule, users and groups only have the rights you explicitly grant them." + - "
2. Each contexted rule comprises these fields: `data`, `rows`, `severity` `additional_checks`, `dataset_rewrites`. The only required fields are `data` and `rows`." + - "
3. The rules block does not need to include all three operation types (reads, updates and deletes); actions you omit are disallowed." + - "
4. If you do not include a hosts block, Cyral does not enforce limits based on the connecting client's host address.", - - CreateContext: resourcePolicyRuleCreate, - ReadContext: resourcePolicyRuleRead, - UpdateContext: resourcePolicyRuleUpdate, - DeleteContext: core.DeleteResource(policyRuleDeleteConfig()), - - SchemaVersion: 1, - StateUpgraders: []schema.StateUpgrader{ - { - Version: 0, - Type: policyRuleResourceSchemaV0(). - CoreConfigSchema().ImpliedType(), - Upgrade: UpgradePolicyRuleV0, - }, - }, - - Schema: policyRuleResourceSchemaV0().Schema, - - Importer: &schema.ResourceImporter{ - StateContext: func( - ctx context.Context, - d *schema.ResourceData, - m interface{}, - ) ([]*schema.ResourceData, error) { - ids, err := utils.UnMarshalComposedID(d.Id(), "/", 2) - if err != nil { - return nil, err - } - policyID := ids[0] - d.Set("policy_id", policyID) - return []*schema.ResourceData{d}, nil - }, - }, - } -} - -func resourcePolicyRuleCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Debug(ctx, "Init resourcePolicyRuleCreate") - c := m.(*client.Client) - - policyID := d.Get("policy_id").(string) - resourceData := getPolicyRuleInfoFromResource(ctx, d) - tflog.Debug(ctx, fmt.Sprintf("resourcePolicyRuleCreate - policyRule: %#v", resourceData)) - - url := fmt.Sprintf("https://%s/v1/policies/%s/rules", c.ControlPlane, policyID) - - body, err := c.DoRequest(ctx, url, http.MethodPost, resourceData) - if err != nil { - return utils.CreateError("Unable to create policy rule", fmt.Sprintf("%v", err)) - } - - response := core.IDBasedResponse{} - if err := json.Unmarshal(body, &response); err != nil { - return utils.CreateError("Unable to unmarshall JSON", fmt.Sprintf("%v", err)) - } - tflog.Debug(ctx, fmt.Sprintf("Response body (unmarshalled): %#v", response)) - - d.SetId(utils.MarshalComposedID([]string{ - policyID, - response.ID}, - "/")) - - tflog.Debug(ctx, "End resourcePolicyRuleCreate") - - return resourcePolicyRuleRead(ctx, d, m) -} - -func resourcePolicyRuleRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Debug(ctx, "Init resourcePolicyRuleRead") - c := m.(*client.Client) - - policyID, policyRuleID := unmarshalPolicyRuleID(d) - url := fmt.Sprintf("https://%s/v1/policies/%s/rules/%s", - c.ControlPlane, policyID, policyRuleID) - - body, err := c.DoRequest(ctx, url, http.MethodGet, nil) - if err != nil { - return utils.CreateError("Unable to read policy rule", fmt.Sprintf("%v", err)) - } - - response := PolicyRule{} - if err := json.Unmarshal(body, &response); err != nil { - return utils.CreateError(fmt.Sprintf("Unable to unmarshall JSON"), fmt.Sprintf("%v", err)) - } - tflog.Debug(ctx, fmt.Sprintf("Response body (unmarshalled): %#v", response)) - - deletes := flattenRulesList(ctx, response.Deletes) - tflog.Debug(ctx, fmt.Sprintf("flattened deletes %#v", deletes)) - if err := d.Set("deletes", deletes); err != nil { - return utils.CreateError("Unable to read policy rule", fmt.Sprintf("%v", err)) - } - - reads := flattenRulesList(ctx, response.Reads) - tflog.Debug(ctx, fmt.Sprintf("flattened reads %#v", reads)) - if err := d.Set("reads", reads); err != nil { - return utils.CreateError("Unable to read policy rule", fmt.Sprintf("%v", err)) - } - - updates := flattenRulesList(ctx, response.Updates) - tflog.Debug(ctx, fmt.Sprintf("flattened updates %#v", updates)) - if err := d.Set("updates", updates); err != nil { - return utils.CreateError("Unable to read policy rule", fmt.Sprintf("%v", err)) - } - - if response.Identities != nil { - if response.Identities.DBRoles != nil || response.Identities.Users != nil || - response.Identities.Groups != nil || response.Identities.Services != nil { - identities := flattenIdentities(ctx, response.Identities) - tflog.Debug(ctx, fmt.Sprintf("flattened identities %#v", identities)) - if err := d.Set("identities", identities); err != nil { - return utils.CreateError("Unable to read policy rule", fmt.Sprintf("%v", err)) - } - } - } - - d.Set("hosts", response.Hosts) - - // Computed arguments - d.Set("policy_rule_id", policyRuleID) - - tflog.Debug(ctx, "resourcePolicyRuleRead") - return diag.Diagnostics{} -} - -func resourcePolicyRuleUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - tflog.Debug(ctx, "resourcePolicyRuleUpdate") - c := m.(*client.Client) - - policyRule := getPolicyRuleInfoFromResource(ctx, d) - - policyID, policyRuleID := unmarshalPolicyRuleID(d) - url := fmt.Sprintf("https://%s/v1/policies/%s/rules/%s", c.ControlPlane, - policyID, policyRuleID) - - _, err := c.DoRequest(ctx, url, http.MethodPut, policyRule) - if err != nil { - return utils.CreateError("Unable to update policy rule", fmt.Sprintf("%v", err)) - } - - tflog.Debug(ctx, "End resourcePolicyRuleUpdate") - - return resourcePolicyRuleRead(ctx, d, m) -} - -func policyRuleDeleteConfig() core.ResourceOperationConfig { - return core.ResourceOperationConfig{ - ResourceName: "PolicyRuleDelete", - Type: operationtype.Delete, - HttpMethod: http.MethodDelete, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - policyID, policyRuleID := unmarshalPolicyRuleID(d) - return fmt.Sprintf("https://%s/v1/policies/%s/rules/%s", - c.ControlPlane, policyID, policyRuleID) - }, - RequestErrorHandler: &core.IgnoreHttpNotFound{ResName: "Policy Rule"}, - } -} - -func getStrListFromInterfaceList(ctx context.Context, interfaceList []interface{}) []string { - tflog.Debug(ctx, "Init getStrListFromInterfaceList") - - strList := []string{} - - for _, i := range interfaceList { - strList = append(strList, i.(string)) - } - - tflog.Debug(ctx, "End getStrListFromInterfaceList") - - return strList -} - -func getDatasetRewrites(ctx context.Context, datasetList []interface{}) []DatasetRewrite { - tflog.Debug(ctx, "Init getDatasetRewrites") - - datasetRewrites := make([]DatasetRewrite, 0, len(datasetList)) - - for _, d := range datasetList { - datasetMap := d.(map[string]interface{}) - - datasetRewrite := DatasetRewrite{ - Dataset: datasetMap["dataset"].(string), - Repo: datasetMap["repo"].(string), - Substitution: datasetMap["substitution"].(string), - Parameters: getStrListFromInterfaceList(ctx, datasetMap["parameters"].([]interface{})), - } - - datasetRewrites = append(datasetRewrites, datasetRewrite) - } - - tflog.Debug(ctx, "End getDatasetRewrites") - - return datasetRewrites -} - -func getRuleListFromResource(ctx context.Context, d *schema.ResourceData, name string) []Rule { - tflog.Debug(ctx, "Init getRuleListFromResource") - ruleInfoList := d.Get(name).([]interface{}) - ruleList := make([]Rule, 0, len(ruleInfoList)) - - for _, ruleInterface := range ruleInfoList { - ruleMap := ruleInterface.(map[string]interface{}) - - rule := Rule{ - AdditionalChecks: ruleMap["additional_checks"].(string), - Data: getStrListFromInterfaceList(ctx, ruleMap["data"].([]interface{})), - DatasetRewrites: getDatasetRewrites(ctx, ruleMap["dataset_rewrites"].([]interface{})), - Rows: ruleMap["rows"].(int), - Severity: ruleMap["severity"].(string), - RateLimit: ruleMap["rate_limit"].(int), - } - - ruleList = append(ruleList, rule) - } - tflog.Debug(ctx, "End getRuleListFromResource") - - return ruleList -} - -func getPolicyRuleInfoFromResource(ctx context.Context, d *schema.ResourceData) PolicyRule { - tflog.Debug(ctx, "Init getPolicyRuleInfoFromResource") - hosts := utils.GetStrListFromSchemaField(d, "hosts") - - identity := d.Get("identities").([]interface{}) - - var identities *Identity - for _, id := range identity { - idMap := id.(map[string]interface{}) - - identities = &Identity{ - DBRoles: getStrListFromInterfaceList(ctx, idMap["db_roles"].([]interface{})), - Groups: getStrListFromInterfaceList(ctx, idMap["groups"].([]interface{})), - Services: getStrListFromInterfaceList(ctx, idMap["services"].([]interface{})), - Users: getStrListFromInterfaceList(ctx, idMap["users"].([]interface{})), - } - } - - policyRule := PolicyRule{ - Deletes: getRuleListFromResource(ctx, d, "deletes"), - Hosts: hosts, - Identities: identities, - Reads: getRuleListFromResource(ctx, d, "reads"), - Updates: getRuleListFromResource(ctx, d, "updates"), - } - - tflog.Debug(ctx, "End getPolicyRuleInfoFromResource") - - return policyRule -} - -func flattenIdentities(ctx context.Context, identities *Identity) []interface{} { - tflog.Debug(ctx, "Init flattenIdentities") - tflog.Debug(ctx, fmt.Sprintf("identities %#v", identities)) - identityMap := make(map[string]interface{}) - - identityMap["db_roles"] = identities.DBRoles - identityMap["groups"] = identities.Groups - identityMap["services"] = identities.Services - identityMap["users"] = identities.Users - - tflog.Debug(ctx, "End flattenIdentities") - return []interface{}{identityMap} -} - -func flattenRulesList(ctx context.Context, rulesList []Rule) []interface{} { - tflog.Debug(ctx, "Init flattenRulesList") - if rulesList != nil { - rules := make([]interface{}, len(rulesList), len(rulesList)) - - for i, rule := range rulesList { - ruleMap := make(map[string]interface{}) - - datasetRewriteList := make([]interface{}, len(rule.DatasetRewrites), len(rule.DatasetRewrites)) - - for j, datasetRewrite := range rule.DatasetRewrites { - drMap := make(map[string]interface{}) - drMap["dataset"] = datasetRewrite.Dataset - drMap["repo"] = datasetRewrite.Repo - drMap["substitution"] = datasetRewrite.Substitution - drMap["parameters"] = datasetRewrite.Parameters - - datasetRewriteList[j] = drMap - } - - ruleMap["additional_checks"] = rule.AdditionalChecks - ruleMap["data"] = rule.Data - ruleMap["dataset_rewrites"] = datasetRewriteList - ruleMap["rows"] = rule.Rows - ruleMap["severity"] = rule.Severity - ruleMap["rate_limit"] = rule.RateLimit - - rules[i] = ruleMap - } - - return rules - } - tflog.Debug(ctx, "End flattenRulesList") - - return make([]interface{}, 0) -} diff --git a/cyral/internal/policy/rule/resource_cyral_policy_rule_test.go b/cyral/internal/policy/rule/resource_test.go similarity index 100% rename from cyral/internal/policy/rule/resource_cyral_policy_rule_test.go rename to cyral/internal/policy/rule/resource_test.go diff --git a/cyral/internal/policy/rule/schema_loader.go b/cyral/internal/policy/rule/schema_loader.go new file mode 100644 index 00000000..35ed8a52 --- /dev/null +++ b/cyral/internal/policy/rule/schema_loader.go @@ -0,0 +1,26 @@ +package rule + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" +) + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "rule" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: resourceName, + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/policy/schema_loader.go b/cyral/internal/policy/schema_loader.go new file mode 100644 index 00000000..3d4fbfda --- /dev/null +++ b/cyral/internal/policy/schema_loader.go @@ -0,0 +1,26 @@ +package policy + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" +) + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "policy" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: resourceName, + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/regopolicy/constants.go b/cyral/internal/regopolicy/constants.go new file mode 100644 index 00000000..95008536 --- /dev/null +++ b/cyral/internal/regopolicy/constants.go @@ -0,0 +1,26 @@ +package regopolicy + +const ( + resourceName = "cyral_rego_policy_instance" +) + +const ( + // Schema keys + RegoPolicyInstanceResourceIDKey = "id" + RegoPolicyInstancePolicyIDKey = "policy_id" + RegoPolicyInstanceCategoryKey = "category" + RegoPolicyInstanceNameKey = "name" + RegoPolicyInstanceDescriptionKey = "description" + RegoPolicyInstanceTemplateIDKey = "template_id" + RegoPolicyInstanceParametersKey = "parameters" + RegoPolicyInstanceEnabledKey = "enabled" + RegoPolicyInstanceScopeKey = "scope" + RegoPolicyInstanceRepoIDsKey = "repo_ids" + RegoPolicyInstanceTagsKey = "tags" + RegoPolicyInstanceDurationKey = "duration" + RegoPolicyInstanceLastUpdatedKey = "last_updated" + RegoPolicyInstanceCreatedKey = "created" + RegoPolicyInstanceActorKey = "actor" + RegoPolicyInstanceActorTypeKey = "actor_type" + RegoPolicyInstanceTimestampKey = "timestamp" +) diff --git a/cyral/internal/regopolicy/model_rego_policy_instance.go b/cyral/internal/regopolicy/model.go similarity index 100% rename from cyral/internal/regopolicy/model_rego_policy_instance.go rename to cyral/internal/regopolicy/model.go diff --git a/cyral/internal/regopolicy/resource_cyral_rego_policy_instance.go b/cyral/internal/regopolicy/resource.go similarity index 71% rename from cyral/internal/regopolicy/resource_cyral_rego_policy_instance.go rename to cyral/internal/regopolicy/resource.go index 865514e8..cc659a77 100644 --- a/cyral/internal/regopolicy/resource_cyral_rego_policy_instance.go +++ b/cyral/internal/regopolicy/resource.go @@ -8,32 +8,34 @@ import ( "github.com/cyralinc/terraform-provider-cyral/cyral/client" "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/operationtype" + "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/resourcetype" "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) -const ( - // Schema keys - RegoPolicyInstanceResourceIDKey = "id" - RegoPolicyInstancePolicyIDKey = "policy_id" - RegoPolicyInstanceCategoryKey = "category" - RegoPolicyInstanceNameKey = "name" - RegoPolicyInstanceDescriptionKey = "description" - RegoPolicyInstanceTemplateIDKey = "template_id" - RegoPolicyInstanceParametersKey = "parameters" - RegoPolicyInstanceEnabledKey = "enabled" - RegoPolicyInstanceScopeKey = "scope" - RegoPolicyInstanceRepoIDsKey = "repo_ids" - RegoPolicyInstanceTagsKey = "tags" - RegoPolicyInstanceDurationKey = "duration" - RegoPolicyInstanceLastUpdatedKey = "last_updated" - RegoPolicyInstanceCreatedKey = "created" - RegoPolicyInstanceActorKey = "actor" - RegoPolicyInstanceActorTypeKey = "actor_type" - RegoPolicyInstanceTimestampKey = "timestamp" -) - +var resourceContextHandler = core.DefaultContextHandler{ + ResourceName: resourceName, + ResourceType: resourcetype.Resource, + SchemaReaderFactory: func() core.SchemaReader { return &RegoPolicyInstancePayload{} }, + SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &RegoPolicyInstance{} }, + SchemaWriterFactoryPostMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &RegoPolicyInstanceKey{} }, + BaseURLFactory: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf( + "https://%s/v1/regopolicies/instances/%s", + c.ControlPlane, + d.Get(RegoPolicyInstanceCategoryKey), + ) + }, + ReadUpdateDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf( + "https://%s/v1/regopolicies/instances/%s/%s", + c.ControlPlane, + d.Get(RegoPolicyInstanceCategoryKey), + d.Get(RegoPolicyInstancePolicyIDKey), + ) + }, +} var ( ReadRegoPolicyInstanceConfig = core.ResourceOperationConfig{ ResourceName: "RegoPolicyInstanceRead", @@ -74,61 +76,15 @@ var ( } ) -func ResourceRegoPolicyInstance() *schema.Resource { +func resourceSchema() *schema.Resource { return &schema.Resource{ Description: "Manages a [Rego Policy](https://cyral.com/docs/policy/rego-policy/overview#) instance." + "\n\n-> **Note** This resource can be used to create repo-level policies by specifying the repo IDs " + "associated to the policy `scope`. For more information, see the [scope](#nestedblock--scope) field.", - CreateContext: core.CreateResource( - core.ResourceOperationConfig{ - ResourceName: "RegoPolicyInstanceCreate", - Type: operationtype.Create, - HttpMethod: http.MethodPost, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf( - "https://%s/v1/regopolicies/instances/%s", - c.ControlPlane, - d.Get(RegoPolicyInstanceCategoryKey), - ) - }, - SchemaReaderFactory: func() core.SchemaReader { return &RegoPolicyInstancePayload{} }, - SchemaWriterFactory: func(_ *schema.ResourceData) core.SchemaWriter { return &RegoPolicyInstanceKey{} }, - }, - ReadRegoPolicyInstanceConfig, - ), - ReadContext: core.ReadResource(ReadRegoPolicyInstanceConfig), - UpdateContext: core.UpdateResource( - core.ResourceOperationConfig{ - ResourceName: "RegoPolicyInstanceUpdate", - Type: operationtype.Update, - HttpMethod: http.MethodPut, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf( - "https://%s/v1/regopolicies/instances/%s/%s", - c.ControlPlane, - d.Get(RegoPolicyInstanceCategoryKey), - d.Get(RegoPolicyInstancePolicyIDKey), - ) - }, - SchemaReaderFactory: func() core.SchemaReader { return &RegoPolicyInstancePayload{} }, - }, - ReadRegoPolicyInstanceConfig, - ), - DeleteContext: core.DeleteResource( - core.ResourceOperationConfig{ - ResourceName: "RegoPolicyInstanceDelete", - Type: operationtype.Delete, - HttpMethod: http.MethodDelete, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf( - "https://%s/v1/regopolicies/instances/%s/%s", - c.ControlPlane, - d.Get(RegoPolicyInstanceCategoryKey), - d.Get(RegoPolicyInstancePolicyIDKey), - ) - }, - }, - ), + CreateContext: resourceContextHandler.CreateContext(), + ReadContext: resourceContextHandler.ReadContext(), + UpdateContext: resourceContextHandler.UpdateContext(), + DeleteContext: resourceContextHandler.DeleteContext(), Schema: map[string]*schema.Schema{ RegoPolicyInstanceResourceIDKey: { diff --git a/cyral/internal/regopolicy/resource_cyral_rego_policy_instance_test.go b/cyral/internal/regopolicy/resource_test.go similarity index 100% rename from cyral/internal/regopolicy/resource_cyral_rego_policy_instance_test.go rename to cyral/internal/regopolicy/resource_test.go diff --git a/cyral/internal/regopolicy/schema_loader.go b/cyral/internal/regopolicy/schema_loader.go new file mode 100644 index 00000000..dcad921f --- /dev/null +++ b/cyral/internal/regopolicy/schema_loader.go @@ -0,0 +1,26 @@ +package regopolicy + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" +) + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "regopolicy" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: resourceName, + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/repository/accessgateway/schema_loader.go b/cyral/internal/repository/accessgateway/schema_loader.go index ce0734f9..6872dcce 100644 --- a/cyral/internal/repository/accessgateway/schema_loader.go +++ b/cyral/internal/repository/accessgateway/schema_loader.go @@ -8,7 +8,7 @@ type packageSchema struct { } func (p *packageSchema) Name() string { - return "accessgateway" + return "repository.accessgateway" } func (p *packageSchema) Schemas() []*core.SchemaDescriptor { diff --git a/cyral/internal/repository/accessrules/schema_loader.go b/cyral/internal/repository/accessrules/schema_loader.go index 314fb9a5..60f43e64 100644 --- a/cyral/internal/repository/accessrules/schema_loader.go +++ b/cyral/internal/repository/accessrules/schema_loader.go @@ -8,7 +8,7 @@ type packageSchema struct { } func (p *packageSchema) Name() string { - return "accessrules" + return "repository.accessrules" } func (p *packageSchema) Schemas() []*core.SchemaDescriptor { diff --git a/cyral/internal/repository/binding/resource.go b/cyral/internal/repository/binding/resource.go index 466a6118..728e9822 100644 --- a/cyral/internal/repository/binding/resource.go +++ b/cyral/internal/repository/binding/resource.go @@ -22,7 +22,7 @@ var resourceContextHandler = core.DefaultContextHandler{ c.ControlPlane, d.Get(utils.SidecarIDKey).(string)) }, - GetPutDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { + ReadUpdateDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/sidecars/%s/bindings/%s", c.ControlPlane, d.Get(utils.SidecarIDKey).(string), diff --git a/cyral/internal/repository/binding/schema_loader.go b/cyral/internal/repository/binding/schema_loader.go index 6f551f7b..3c23ea4c 100644 --- a/cyral/internal/repository/binding/schema_loader.go +++ b/cyral/internal/repository/binding/schema_loader.go @@ -8,7 +8,7 @@ type packageSchema struct { } func (p *packageSchema) Name() string { - return "binding" + return "repository.binding" } func (p *packageSchema) Schemas() []*core.SchemaDescriptor { diff --git a/cyral/internal/repository/confanalysis/resource.go b/cyral/internal/repository/confanalysis/resource.go index c55fe231..044497a3 100644 --- a/cyral/internal/repository/confanalysis/resource.go +++ b/cyral/internal/repository/confanalysis/resource.go @@ -31,7 +31,7 @@ var resourceContextHandler = core.DefaultContextHandler{ SchemaReaderFactory: func() core.SchemaReader { return &UserConfig{} }, SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &RepositoryConfAnalysisData{} }, BaseURLFactory: urlFactory, - GetPutDeleteURLFactory: urlFactory, + ReadUpdateDeleteURLFactory: urlFactory, } func resourceSchema() *schema.Resource { diff --git a/cyral/internal/repository/confanalysis/schema_loader.go b/cyral/internal/repository/confanalysis/schema_loader.go index 5eec9304..9e235935 100644 --- a/cyral/internal/repository/confanalysis/schema_loader.go +++ b/cyral/internal/repository/confanalysis/schema_loader.go @@ -8,7 +8,7 @@ type packageSchema struct { } func (p *packageSchema) Name() string { - return "confanalysis" + return "repository.confanalysis" } func (p *packageSchema) Schemas() []*core.SchemaDescriptor { diff --git a/cyral/internal/repository/confauth/resource.go b/cyral/internal/repository/confauth/resource.go index 6a457482..b08ef413 100644 --- a/cyral/internal/repository/confauth/resource.go +++ b/cyral/internal/repository/confauth/resource.go @@ -34,7 +34,7 @@ var resourceContextHandler = core.DefaultContextHandler{ SchemaReaderFactory: func() core.SchemaReader { return &RepositoryConfAuthData{} }, SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &ReadRepositoryConfAuthResponse{} }, BaseURLFactory: urlFactory, - GetPutDeleteURLFactory: urlFactory, + ReadUpdateDeleteURLFactory: urlFactory, } func resourceSchema() *schema.Resource { diff --git a/cyral/internal/repository/confauth/schema_loader.go b/cyral/internal/repository/confauth/schema_loader.go index 4066051c..707844a9 100644 --- a/cyral/internal/repository/confauth/schema_loader.go +++ b/cyral/internal/repository/confauth/schema_loader.go @@ -8,7 +8,7 @@ type packageSchema struct { } func (p *packageSchema) Name() string { - return "confauth" + return "repository.confauth" } func (p *packageSchema) Schemas() []*core.SchemaDescriptor { diff --git a/cyral/internal/repository/datamap/schema_loader.go b/cyral/internal/repository/datamap/schema_loader.go index 4810b2e5..a8100fce 100644 --- a/cyral/internal/repository/datamap/schema_loader.go +++ b/cyral/internal/repository/datamap/schema_loader.go @@ -8,7 +8,7 @@ type packageSchema struct { } func (p *packageSchema) Name() string { - return "datamap" + return "repository.datamap" } func (p *packageSchema) Schemas() []*core.SchemaDescriptor { diff --git a/cyral/internal/repository/datasource.go b/cyral/internal/repository/datasource.go index 914f92dd..cf02d63d 100644 --- a/cyral/internal/repository/datasource.go +++ b/cyral/internal/repository/datasource.go @@ -61,7 +61,7 @@ var dsContextHandler = core.DefaultContextHandler{ ResourceName: dataSourceName, ResourceType: resourcetype.DataSource, SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &GetReposResponse{} }, - GetPutDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { + ReadUpdateDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { nameFilter := d.Get("name").(string) typeFilter := d.Get("type").(string) urlParams := utils.UrlQuery(map[string]string{ diff --git a/cyral/internal/repository/network/resource.go b/cyral/internal/repository/network/resource.go index 69fa3424..087b835c 100644 --- a/cyral/internal/repository/network/resource.go +++ b/cyral/internal/repository/network/resource.go @@ -25,7 +25,7 @@ var resourceContextHandler = core.DefaultContextHandler{ SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &NetworkAccessPolicy{} }, SchemaWriterFactoryPostMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &NetworkAccessPolicyUpsertResp{} }, BaseURLFactory: urlFactory, - GetPutDeleteURLFactory: urlFactory, + ReadUpdateDeleteURLFactory: urlFactory, } func resourceSchema() *schema.Resource { diff --git a/cyral/internal/repository/network/schema_loader.go b/cyral/internal/repository/network/schema_loader.go index 6444de7c..fb181cdd 100644 --- a/cyral/internal/repository/network/schema_loader.go +++ b/cyral/internal/repository/network/schema_loader.go @@ -8,7 +8,7 @@ type packageSchema struct { } func (p *packageSchema) Name() string { - return "network" + return "repository.network" } func (p *packageSchema) Schemas() []*core.SchemaDescriptor { diff --git a/cyral/internal/repository/useraccount/schema_loader.go b/cyral/internal/repository/useraccount/schema_loader.go index 86b59280..5aa6534e 100644 --- a/cyral/internal/repository/useraccount/schema_loader.go +++ b/cyral/internal/repository/useraccount/schema_loader.go @@ -6,7 +6,7 @@ type packageSchema struct { } func (p *packageSchema) Name() string { - return "useraccount" + return "repository.useraccount" } func (p *packageSchema) Schemas() []*core.SchemaDescriptor { diff --git a/cyral/internal/role/constants.go b/cyral/internal/role/constants.go new file mode 100644 index 00000000..4c16bd05 --- /dev/null +++ b/cyral/internal/role/constants.go @@ -0,0 +1,23 @@ +package role + +const ( + resourceName = "cyral_role" + dataSourceName = "cyral_role" +) + +const ( + // Schema keys + approvalManagementPermissionKey = "approval_management" + modifyPoliciesPermissionKey = "modify_policies" + modifyRolesPermissionKey = "modify_roles" + modifySidecarAndRepositoriesPermissionKey = "modify_sidecars_and_repositories" + modifyUsersPermissionKey = "modify_users" + repoCrawlerPermissionKey = "repo_crawler" + viewAuditLogsPermissionKey = "view_audit_logs" + viewDatamapsPermissionKey = "view_datamaps" + viewIntegrationsPermissionKey = "view_integrations" + viewPoliciesPermissionKey = "view_policies" + viewRolesPermissionKey = "view_roles" + viewUsersPermissionKey = "view_users" + modifyIntegrationsPermissionKey = "modify_integrations" +) diff --git a/cyral/internal/role/data_source_cyral_role.go b/cyral/internal/role/datasource.go similarity index 92% rename from cyral/internal/role/data_source_cyral_role.go rename to cyral/internal/role/datasource.go index 21f367bf..70d3fb58 100644 --- a/cyral/internal/role/data_source_cyral_role.go +++ b/cyral/internal/role/datasource.go @@ -14,6 +14,7 @@ import ( "github.com/cyralinc/terraform-provider-cyral/cyral/client" "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/operationtype" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/role/ssogroups" ) type GetUserGroupsResponse struct { @@ -73,12 +74,12 @@ func (resp *GetUserGroupsResponse) WriteToSchema(d *schema.ResourceData) error { } type UserGroup struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - Description string `json:"description,omitempty"` - Roles []string `json:"roles,omitempty"` - Members []string `json:"members"` - Mappings []*SSOGroup `json:"mappings"` + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Roles []string `json:"roles,omitempty"` + Members []string `json:"members"` + Mappings []*ssogroups.SSOGroup `json:"mappings"` } func dataSourceRoleReadConfig() core.ResourceOperationConfig { @@ -93,7 +94,7 @@ func dataSourceRoleReadConfig() core.ResourceOperationConfig { } } -func DataSourceRole() *schema.Resource { +func dataSourceSchema() *schema.Resource { return &schema.Resource{ Description: "Retrieve and filter [roles](https://cyral.com/docs/account-administration/acct-manage-cyral-roles/) that exist in the Cyral Control Plane.", ReadContext: core.ReadResource(dataSourceRoleReadConfig()), diff --git a/cyral/internal/role/data_source_cyral_role_test.go b/cyral/internal/role/datasource_test.go similarity index 100% rename from cyral/internal/role/data_source_cyral_role_test.go rename to cyral/internal/role/datasource_test.go diff --git a/cyral/internal/role/model.go b/cyral/internal/role/model.go new file mode 100644 index 00000000..8867244d --- /dev/null +++ b/cyral/internal/role/model.go @@ -0,0 +1,20 @@ +package role + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/permission" +) + +// Roles correspond to Groups in API. +type RoleDataRequest struct { + Name string `json:"name,omitempty"` + // Permissions correspond to Roles in API. + PermissionIDs []string `json:"roles,omitempty"` +} + +// Roles correspond to Groups in API. +type RoleDataResponse struct { + Id string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + // Permissions correspond to Roles in API. + Permissions []*permission.Permission `json:"roles,omitempty"` +} diff --git a/cyral/internal/role/model_role.go b/cyral/internal/role/model_role.go deleted file mode 100644 index f7280bef..00000000 --- a/cyral/internal/role/model_role.go +++ /dev/null @@ -1,103 +0,0 @@ -package role - -import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - -const ( - // Schema keys - approvalManagementPermissionKey = "approval_management" - modifyPoliciesPermissionKey = "modify_policies" - modifyRolesPermissionKey = "modify_roles" - modifySidecarAndRepositoriesPermissionKey = "modify_sidecars_and_repositories" - modifyUsersPermissionKey = "modify_users" - repoCrawlerPermissionKey = "repo_crawler" - viewAuditLogsPermissionKey = "view_audit_logs" - viewDatamapsPermissionKey = "view_datamaps" - viewIntegrationsPermissionKey = "view_integrations" - viewPoliciesPermissionKey = "view_policies" - viewRolesPermissionKey = "view_roles" - viewUsersPermissionKey = "view_users" - modifyIntegrationsPermissionKey = "modify_integrations" -) - -var permissionsSchema = map[string]*schema.Schema{ - approvalManagementPermissionKey: { - Description: "Allows approving or denying approval requests on Cyral Control Plane. " + - "Defaults to `false`.", - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - modifyPoliciesPermissionKey: { - Description: "Allows modifying policies on Cyral Control Plane. Defaults to `false`.", - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - modifyRolesPermissionKey: { - Description: "Allows modifying roles on Cyral Control Plane. Defaults to `false`.", - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - modifySidecarAndRepositoriesPermissionKey: { - Description: "Allows modifying sidecars and repositories on Cyral Control Plane. Defaults to `false`.", - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - modifyUsersPermissionKey: { - Description: "Allows modifying users on Cyral Control Plane. Defaults to `false`.", - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - repoCrawlerPermissionKey: { - Description: "Allows running the Cyral repo crawler data classifier and user discovery. " + - "Defaults to `false`.", - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - viewAuditLogsPermissionKey: { - Description: "Allows viewing audit logs on Cyral Control Plane. Defaults to `false`.", - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - viewDatamapsPermissionKey: { - Description: "Allows viewing datamaps on Cyral Control Plane. Defaults to `false`.", - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - viewIntegrationsPermissionKey: { - Description: "Allows viewing integrations on Cyral Control Plane. Defaults to `false`.", - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - viewPoliciesPermissionKey: { - Description: "Allows viewing policies on Cyral Control Plane. Defaults to `false`.", - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - viewRolesPermissionKey: { - Description: "Allows viewing roles on Cyral Control Plane. Defaults to `false`.", - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - viewUsersPermissionKey: { - Description: "Allows viewing users on Cyral Control Plane. Defaults to `false`.", - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - modifyIntegrationsPermissionKey: { - Description: "Allows modifying integrations on Cyral Control Plane. Defaults to `false`.", - Type: schema.TypeBool, - Optional: true, - Default: false, - }, -} diff --git a/cyral/internal/role/resource_cyral_role.go b/cyral/internal/role/resource.go similarity index 63% rename from cyral/internal/role/resource_cyral_role.go rename to cyral/internal/role/resource.go index 4eff2830..368ff38e 100644 --- a/cyral/internal/role/resource_cyral_role.go +++ b/cyral/internal/role/resource.go @@ -15,24 +15,14 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -// Roles correspond to Groups in API. -type RoleDataRequest struct { - Name string `json:"name,omitempty"` - // Permissions correspond to Roles in API. - PermissionIDs []string `json:"roles,omitempty"` +var urlFactory = func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/users/groups/%s", c.ControlPlane, d.Id()) } -// Roles correspond to Groups in API. -type RoleDataResponse struct { - Id string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - // Permissions correspond to Roles in API. - Permissions []*permission.Permission `json:"roles,omitempty"` -} - -func ResourceRole() *schema.Resource { +func resourceSchema() *schema.Resource { return &schema.Resource{ - Description: "Manages [roles for Cyral control plane users](https://cyral.com/docs/account-administration/acct-manage-cyral-roles/#create-and-manage-administrator-roles-for-cyral-control-plane-users). See also: [Role SSO Groups](./role_sso_groups.md).", + Description: "Manages [roles for Cyral control plane users](https://cyral.com/docs/account-administration/acct-manage-cyral-roles/#create-and-manage-administrator-roles-for-cyral-control-plane-users). See also: [Role SSO Groups](./role_sso_groups.md).", + CreateContext: resourceRoleCreate, ReadContext: resourceRoleRead, UpdateContext: resourceRoleUpdate, @@ -55,7 +45,88 @@ func ResourceRole() *schema.Resource { Optional: true, MaxItems: 1, Elem: &schema.Resource{ - Schema: permissionsSchema, + Schema: map[string]*schema.Schema{ + approvalManagementPermissionKey: { + Description: "Allows approving or denying approval requests on Cyral Control Plane. " + + "Defaults to `false`.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + modifyPoliciesPermissionKey: { + Description: "Allows modifying policies on Cyral Control Plane. Defaults to `false`.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + modifyRolesPermissionKey: { + Description: "Allows modifying roles on Cyral Control Plane. Defaults to `false`.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + modifySidecarAndRepositoriesPermissionKey: { + Description: "Allows modifying sidecars and repositories on Cyral Control Plane. Defaults to `false`.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + modifyUsersPermissionKey: { + Description: "Allows modifying users on Cyral Control Plane. Defaults to `false`.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + repoCrawlerPermissionKey: { + Description: "Allows running the Cyral repo crawler data classifier and user discovery. " + + "Defaults to `false`.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + viewAuditLogsPermissionKey: { + Description: "Allows viewing audit logs on Cyral Control Plane. Defaults to `false`.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + viewDatamapsPermissionKey: { + Description: "Allows viewing datamaps on Cyral Control Plane. Defaults to `false`.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + viewIntegrationsPermissionKey: { + Description: "Allows viewing integrations on Cyral Control Plane. Defaults to `false`.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + viewPoliciesPermissionKey: { + Description: "Allows viewing policies on Cyral Control Plane. Defaults to `false`.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + viewRolesPermissionKey: { + Description: "Allows viewing roles on Cyral Control Plane. Defaults to `false`.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + viewUsersPermissionKey: { + Description: "Allows viewing users on Cyral Control Plane. Defaults to `false`.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + modifyIntegrationsPermissionKey: { + Description: "Allows modifying integrations on Cyral Control Plane. Defaults to `false`.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, }, }, }, diff --git a/cyral/internal/role/resource_cyral_role_test.go b/cyral/internal/role/resource_test.go similarity index 100% rename from cyral/internal/role/resource_cyral_role_test.go rename to cyral/internal/role/resource_test.go diff --git a/cyral/internal/role/schema_loader.go b/cyral/internal/role/schema_loader.go new file mode 100644 index 00000000..fcb2dd5c --- /dev/null +++ b/cyral/internal/role/schema_loader.go @@ -0,0 +1,29 @@ +package role + +import "github.com/cyralinc/terraform-provider-cyral/cyral/core" + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "role" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: dataSourceName, + Type: core.DataSourceSchemaType, + Schema: dataSourceSchema, + }, + { + Name: resourceName, + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/role/ssogroups/constants.go b/cyral/internal/role/ssogroups/constants.go new file mode 100644 index 00000000..3fd8db12 --- /dev/null +++ b/cyral/internal/role/ssogroups/constants.go @@ -0,0 +1,5 @@ +package ssogroups + +const ( + resourceName = "cyral_role_sso_groups" +) diff --git a/cyral/internal/role/ssogroups/model.go b/cyral/internal/role/ssogroups/model.go new file mode 100644 index 00000000..783d3c5a --- /dev/null +++ b/cyral/internal/role/ssogroups/model.go @@ -0,0 +1,98 @@ +package ssogroups + +import ( + "encoding/json" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type RoleSSOGroupsCreateRequest struct { + SSOGroupsMappings []*SSOGroup `json:"mappings,omitempty"` +} + +type RoleSSOGroupsReadResponse struct { + SSOGroupsMappings []*SSOGroup `json:"mappings,omitempty"` +} + +type SSOGroup struct { + Id string `json:"id,omitempty"` + GroupName string `json:"groupName,omitempty"` + // IdentityProviderId corresponds to ConnectionAlias in API. + IdentityProviderId string `json:"connectionAlias,omitempty"` + // IdentityProviderName corresponds to ConnectionName in API. + IdentityProviderName string `json:"connectionName,omitempty"` +} + +type RoleSSOGroupsDeleteRequest struct { + SSOGroupsMappingsIds []string `json:"mappings,omitempty"` +} + +func (data RoleSSOGroupsCreateRequest) WriteToSchema(d *schema.ResourceData) error { + d.SetId(d.Get("role_id").(string)) + return nil +} + +func (data *RoleSSOGroupsCreateRequest) ReadFromSchema(d *schema.ResourceData) error { + var SSOGroupsMappings []*SSOGroup + + if ssoGroups, ok := d.GetOk("sso_group"); ok { + ssoGroups := ssoGroups.(*schema.Set).List() + + for _, ssoGroup := range ssoGroups { + ssoGroup := ssoGroup.(map[string]interface{}) + + SSOGroupsMappings = append(SSOGroupsMappings, &SSOGroup{ + GroupName: ssoGroup["group_name"].(string), + IdentityProviderId: ssoGroup["idp_id"].(string), + }) + } + } + + data.SSOGroupsMappings = SSOGroupsMappings + + return nil +} + +func (data RoleSSOGroupsCreateRequest) MarshalJSON() ([]byte, error) { + return json.Marshal(data.SSOGroupsMappings) +} + +func (data RoleSSOGroupsReadResponse) WriteToSchema(d *schema.ResourceData) error { + var flatSSOGroupsMappings []interface{} + + for _, ssoGroup := range data.SSOGroupsMappings { + ssoGroupMap := make(map[string]interface{}) + ssoGroupMap["id"] = ssoGroup.Id + ssoGroupMap["group_name"] = ssoGroup.GroupName + ssoGroupMap["idp_id"] = ssoGroup.IdentityProviderId + ssoGroupMap["idp_name"] = ssoGroup.IdentityProviderName + + flatSSOGroupsMappings = append(flatSSOGroupsMappings, ssoGroupMap) + } + + d.Set("sso_group", flatSSOGroupsMappings) + + return nil +} + +func (data *RoleSSOGroupsDeleteRequest) ReadFromSchema(d *schema.ResourceData) error { + var SSOGroupsMappingsIds []string + + if ssoGroups, ok := d.GetOk("sso_group"); ok { + ssoGroups := ssoGroups.(*schema.Set).List() + + for _, ssoGroup := range ssoGroups { + ssoGroup := ssoGroup.(map[string]interface{}) + + SSOGroupsMappingsIds = append(SSOGroupsMappingsIds, ssoGroup["id"].(string)) + } + } + + data.SSOGroupsMappingsIds = SSOGroupsMappingsIds + + return nil +} + +func (data RoleSSOGroupsDeleteRequest) MarshalJSON() ([]byte, error) { + return json.Marshal(data.SSOGroupsMappingsIds) +} diff --git a/cyral/internal/role/resource_cyral_role_sso_groups.go b/cyral/internal/role/ssogroups/resource.go similarity index 62% rename from cyral/internal/role/resource_cyral_role_sso_groups.go rename to cyral/internal/role/ssogroups/resource.go index 84a888f1..4867ee61 100644 --- a/cyral/internal/role/resource_cyral_role_sso_groups.go +++ b/cyral/internal/role/ssogroups/resource.go @@ -1,8 +1,7 @@ -package role +package ssogroups import ( "context" - "encoding/json" "fmt" "net/http" @@ -12,25 +11,36 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -type RoleSSOGroupsCreateRequest struct { - SSOGroupsMappings []*SSOGroup `json:"mappings,omitempty"` -} +func resourceSchema() *schema.Resource { + return &schema.Resource{ + Description: "Manages [mapping SSO groups to specific roles](https://cyral.com/docs/account-administration/acct-manage-cyral-roles/#map-an-sso-group-to-a-cyral-administrator-role) on Cyral control plane. See also: [Role](./role.md).", + CreateContext: core.CreateResource(createRoleSSOGroupsConfig, readRoleSSOGroupsConfig), + ReadContext: core.ReadResource(readRoleSSOGroupsConfig), + DeleteContext: core.DeleteResource(deleteRoleSSOGroupsConfig), -type RoleSSOGroupsReadResponse struct { - SSOGroupsMappings []*SSOGroup `json:"mappings,omitempty"` -} + SchemaVersion: 1, + StateUpgraders: []schema.StateUpgrader{ + { + Version: 0, + Type: roleSSOGroupsResourceSchemaV0(). + CoreConfigSchema().ImpliedType(), + Upgrade: UpgradeRoleSSOGroupsV0, + }, + }, -type SSOGroup struct { - Id string `json:"id,omitempty"` - GroupName string `json:"groupName,omitempty"` - // IdentityProviderId corresponds to ConnectionAlias in API. - IdentityProviderId string `json:"connectionAlias,omitempty"` - // IdentityProviderName corresponds to ConnectionName in API. - IdentityProviderName string `json:"connectionName,omitempty"` -} + Schema: roleSSOGroupsResourceSchemaV0().Schema, -type RoleSSOGroupsDeleteRequest struct { - SSOGroupsMappingsIds []string `json:"mappings,omitempty"` + Importer: &schema.ResourceImporter{ + StateContext: func( + ctx context.Context, + d *schema.ResourceData, + m interface{}, + ) ([]*schema.ResourceData, error) { + d.Set("role_id", d.Id()) + return []*schema.ResourceData{d}, nil + }, + }, + } } func roleSSOGroupsResourceSchemaV0() *schema.Resource { @@ -90,38 +100,6 @@ func UpgradeRoleSSOGroupsV0( return rawState, nil } -func ResourceRoleSSOGroups() *schema.Resource { - return &schema.Resource{ - Description: "Manages [mapping SSO groups to specific roles](https://cyral.com/docs/account-administration/acct-manage-cyral-roles/#map-an-sso-group-to-a-cyral-administrator-role) on Cyral control plane. See also: [Role](./role.md).", - CreateContext: core.CreateResource(createRoleSSOGroupsConfig, readRoleSSOGroupsConfig), - ReadContext: core.ReadResource(readRoleSSOGroupsConfig), - DeleteContext: core.DeleteResource(deleteRoleSSOGroupsConfig), - - SchemaVersion: 1, - StateUpgraders: []schema.StateUpgrader{ - { - Version: 0, - Type: roleSSOGroupsResourceSchemaV0(). - CoreConfigSchema().ImpliedType(), - Upgrade: UpgradeRoleSSOGroupsV0, - }, - }, - - Schema: roleSSOGroupsResourceSchemaV0().Schema, - - Importer: &schema.ResourceImporter{ - StateContext: func( - ctx context.Context, - d *schema.ResourceData, - m interface{}, - ) ([]*schema.ResourceData, error) { - d.Set("role_id", d.Id()) - return []*schema.ResourceData{d}, nil - }, - }, - } -} - var createRoleSSOGroupsConfig = core.ResourceOperationConfig{ ResourceName: "resourceRoleSSOGroupsCreate", Type: operationtype.Create, @@ -156,73 +134,3 @@ var deleteRoleSSOGroupsConfig = core.ResourceOperationConfig{ }, SchemaReaderFactory: func() core.SchemaReader { return &RoleSSOGroupsDeleteRequest{} }, } - -func (data RoleSSOGroupsCreateRequest) WriteToSchema(d *schema.ResourceData) error { - d.SetId(d.Get("role_id").(string)) - return nil -} - -func (data *RoleSSOGroupsCreateRequest) ReadFromSchema(d *schema.ResourceData) error { - var SSOGroupsMappings []*SSOGroup - - if ssoGroups, ok := d.GetOk("sso_group"); ok { - ssoGroups := ssoGroups.(*schema.Set).List() - - for _, ssoGroup := range ssoGroups { - ssoGroup := ssoGroup.(map[string]interface{}) - - SSOGroupsMappings = append(SSOGroupsMappings, &SSOGroup{ - GroupName: ssoGroup["group_name"].(string), - IdentityProviderId: ssoGroup["idp_id"].(string), - }) - } - } - - data.SSOGroupsMappings = SSOGroupsMappings - - return nil -} - -func (data RoleSSOGroupsCreateRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(data.SSOGroupsMappings) -} - -func (data RoleSSOGroupsReadResponse) WriteToSchema(d *schema.ResourceData) error { - var flatSSOGroupsMappings []interface{} - - for _, ssoGroup := range data.SSOGroupsMappings { - ssoGroupMap := make(map[string]interface{}) - ssoGroupMap["id"] = ssoGroup.Id - ssoGroupMap["group_name"] = ssoGroup.GroupName - ssoGroupMap["idp_id"] = ssoGroup.IdentityProviderId - ssoGroupMap["idp_name"] = ssoGroup.IdentityProviderName - - flatSSOGroupsMappings = append(flatSSOGroupsMappings, ssoGroupMap) - } - - d.Set("sso_group", flatSSOGroupsMappings) - - return nil -} - -func (data *RoleSSOGroupsDeleteRequest) ReadFromSchema(d *schema.ResourceData) error { - var SSOGroupsMappingsIds []string - - if ssoGroups, ok := d.GetOk("sso_group"); ok { - ssoGroups := ssoGroups.(*schema.Set).List() - - for _, ssoGroup := range ssoGroups { - ssoGroup := ssoGroup.(map[string]interface{}) - - SSOGroupsMappingsIds = append(SSOGroupsMappingsIds, ssoGroup["id"].(string)) - } - } - - data.SSOGroupsMappingsIds = SSOGroupsMappingsIds - - return nil -} - -func (data RoleSSOGroupsDeleteRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(data.SSOGroupsMappingsIds) -} diff --git a/cyral/internal/role/resource_cyral_role_sso_groups_test.go b/cyral/internal/role/ssogroups/resource_test.go similarity index 98% rename from cyral/internal/role/resource_cyral_role_sso_groups_test.go rename to cyral/internal/role/ssogroups/resource_test.go index 72d2f915..ffa22665 100644 --- a/cyral/internal/role/resource_cyral_role_sso_groups_test.go +++ b/cyral/internal/role/ssogroups/resource_test.go @@ -1,4 +1,4 @@ -package role_test +package ssogroups_test import ( "context" @@ -6,7 +6,7 @@ import ( "regexp" "testing" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/role" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/role/ssogroups" "github.com/cyralinc/terraform-provider-cyral/cyral/provider" "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -87,7 +87,7 @@ func TestRoleSSOGroupsResourceUpgradeV0(t *testing.T) { "id": "roleID/SSOGroups", "role_id": "roleID", } - actualNewState, err := role.UpgradeRoleSSOGroupsV0(context.Background(), + actualNewState, err := ssogroups.UpgradeRoleSSOGroupsV0(context.Background(), previousState, nil) require.NoError(t, err) expectedNewState := map[string]interface{}{ diff --git a/cyral/internal/role/ssogroups/schema_loader.go b/cyral/internal/role/ssogroups/schema_loader.go new file mode 100644 index 00000000..395a3a8d --- /dev/null +++ b/cyral/internal/role/ssogroups/schema_loader.go @@ -0,0 +1,24 @@ +package ssogroups + +import "github.com/cyralinc/terraform-provider-cyral/cyral/core" + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "role.ssogroups" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: resourceName, + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/serviceaccount/constants.go b/cyral/internal/serviceaccount/constants.go new file mode 100644 index 00000000..ce653d32 --- /dev/null +++ b/cyral/internal/serviceaccount/constants.go @@ -0,0 +1,11 @@ +package serviceaccount + +const ( + resourceName = "cyral_service_account" + + // Schema keys + ServiceAccountResourceDisplayNameKey = "display_name" + ServiceAccountResourcePermissionIDsKey = "permission_ids" + ServiceAccountResourceClientIDKey = "client_id" + ServiceAccountResourceClientSecretKey = "client_secret" +) diff --git a/cyral/internal/serviceaccount/model_service_account.go b/cyral/internal/serviceaccount/model.go similarity index 94% rename from cyral/internal/serviceaccount/model_service_account.go rename to cyral/internal/serviceaccount/model.go index d653f087..48da3cb7 100644 --- a/cyral/internal/serviceaccount/model_service_account.go +++ b/cyral/internal/serviceaccount/model.go @@ -27,7 +27,7 @@ func (serviceAccount *ServiceAccount) ReadFromSchema(d *schema.ResourceData) err return nil } -func (serviceAccount *ServiceAccount) WriteToSchema(d *schema.ResourceData) error { +func (serviceAccount ServiceAccount) WriteToSchema(d *schema.ResourceData) error { d.SetId(serviceAccount.ClientID) d.Set(ServiceAccountResourceDisplayNameKey, serviceAccount.DisplayName) d.Set(ServiceAccountResourceClientIDKey, serviceAccount.ClientID) diff --git a/cyral/internal/serviceaccount/resource.go b/cyral/internal/serviceaccount/resource.go new file mode 100644 index 00000000..7966dd5d --- /dev/null +++ b/cyral/internal/serviceaccount/resource.go @@ -0,0 +1,77 @@ +package serviceaccount + +import ( + "fmt" + "net/http" + + "github.com/cyralinc/terraform-provider-cyral/cyral/client" + "github.com/cyralinc/terraform-provider-cyral/cyral/core" + "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/resourcetype" + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var resourceContextHandler = core.DefaultContextHandler{ + ResourceName: resourceName, + ResourceType: resourcetype.Resource, + SchemaReaderFactory: func() core.SchemaReader { return &ServiceAccount{} }, + SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &ServiceAccount{} }, + SchemaWriterFactoryPostMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &ServiceAccount{} }, + BaseURLFactory: func(d *schema.ResourceData, c *client.Client) string { + return fmt.Sprintf("https://%s/v1/users/serviceAccounts", c.ControlPlane) + }, + UpdateMethod: http.MethodPatch, +} + +func resourceSchema() *schema.Resource { + return &schema.Resource{ + Description: "Manages a Cyral Service Account (A.k.a: " + + "[Cyral API Access Key](https://cyral.com/docs/api-ref/api-intro/#api-access-key)). See also " + + "data source [`cyral_permission`](../data-sources/permission.md)." + + "\n\n-> **Note** This resource does not support importing, since the client secret cannot " + + "be read after the resource creation.", + CreateContext: resourceContextHandler.CreateContext(), + ReadContext: resourceContextHandler.ReadContext(), + UpdateContext: resourceContextHandler.UpdateContext(), + DeleteContext: resourceContextHandler.DeleteContext(), + + Schema: map[string]*schema.Schema{ + ServiceAccountResourceDisplayNameKey: { + Description: "The service account display name.", + Type: schema.TypeString, + Required: true, + }, + ServiceAccountResourcePermissionIDsKey: { + Description: "A list of permission IDs that will be assigned to this service account. See " + + "also data source [`cyral_permission`](../data-sources/permission.md).", + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + utils.IDKey: { + Description: fmt.Sprintf( + "The resource identifier. It's equal to `%s`.", + ServiceAccountResourceClientIDKey, + ), + Type: schema.TypeString, + Computed: true, + }, + ServiceAccountResourceClientIDKey: { + Description: "The service account client ID.", + Type: schema.TypeString, + Computed: true, + }, + ServiceAccountResourceClientSecretKey: { + Description: "The service account client secret. **Note**: This resource is not able to recognize " + + "changes to the client secret after its creation, so keep in mind that if the client secret is " + + "rotated, the value present in this attribute will be outdated. If you need to rotate the client " + + "secret it's recommended that you recreate this terraform resource.", + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + }, + } +} diff --git a/cyral/internal/serviceaccount/resource_cyral_service_account.go b/cyral/internal/serviceaccount/resource_cyral_service_account.go deleted file mode 100644 index 8d0c7da4..00000000 --- a/cyral/internal/serviceaccount/resource_cyral_service_account.go +++ /dev/null @@ -1,134 +0,0 @@ -package serviceaccount - -import ( - "fmt" - "net/http" - - "github.com/cyralinc/terraform-provider-cyral/cyral/client" - "github.com/cyralinc/terraform-provider-cyral/cyral/core" - "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/operationtype" - "github.com/cyralinc/terraform-provider-cyral/cyral/utils" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -const ( - // Schema keys - ServiceAccountResourceDisplayNameKey = "display_name" - ServiceAccountResourcePermissionIDsKey = "permission_ids" - ServiceAccountResourceClientIDKey = "client_id" - ServiceAccountResourceClientSecretKey = "client_secret" -) - -var ( - ReadServiceAccountConfig = core.ResourceOperationConfig{ - ResourceName: "ServiceAccountRead", - HttpMethod: http.MethodGet, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf( - "https://%s/v1/users/serviceAccounts/%s", - c.ControlPlane, - d.Id(), - ) - }, - SchemaWriterFactory: func(_ *schema.ResourceData) core.SchemaWriter { - return &ServiceAccount{} - }, - RequestErrorHandler: &core.IgnoreHttpNotFound{ResName: "Service account"}, - } -) - -func ResourceServiceAccount() *schema.Resource { - return &schema.Resource{ - Description: "Manages a Cyral Service Account (A.k.a: " + - "[Cyral API Access Key](https://cyral.com/docs/api-ref/api-intro/#api-access-key)). See also " + - "data source [`cyral_permission`](../data-sources/permission.md)." + - "\n\n-> **Note** This resource does not support importing, since the client secret cannot " + - "be read after the resource creation.", - CreateContext: core.CreateResource( - core.ResourceOperationConfig{ - ResourceName: "ServiceAccountCreate", - Type: operationtype.Create, - HttpMethod: http.MethodPost, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf( - "https://%s/v1/users/serviceAccounts", - c.ControlPlane, - ) - }, - SchemaReaderFactory: func() core.SchemaReader { return &ServiceAccount{} }, - SchemaWriterFactory: func(_ *schema.ResourceData) core.SchemaWriter { return &ServiceAccount{} }, - }, - ReadServiceAccountConfig, - ), - ReadContext: core.ReadResource(ReadServiceAccountConfig), - UpdateContext: core.UpdateResource( - core.ResourceOperationConfig{ - ResourceName: "ServiceAccountUpdate", - Type: operationtype.Update, - HttpMethod: http.MethodPatch, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf( - "https://%s/v1/users/serviceAccounts/%s", - c.ControlPlane, - d.Id(), - ) - }, - SchemaReaderFactory: func() core.SchemaReader { return &ServiceAccount{} }, - }, - ReadServiceAccountConfig, - ), - DeleteContext: core.DeleteResource( - core.ResourceOperationConfig{ - ResourceName: "ServiceAccountDelete", - Type: operationtype.Delete, - HttpMethod: http.MethodDelete, - URLFactory: func(d *schema.ResourceData, c *client.Client) string { - return fmt.Sprintf( - "https://%s/v1/users/serviceAccounts/%s", - c.ControlPlane, - d.Id(), - ) - }, - }, - ), - - Schema: map[string]*schema.Schema{ - ServiceAccountResourceDisplayNameKey: { - Description: "The service account display name.", - Type: schema.TypeString, - Required: true, - }, - ServiceAccountResourcePermissionIDsKey: { - Description: "A list of permission IDs that will be assigned to this service account. See " + - "also data source [`cyral_permission`](../data-sources/permission.md).", - Type: schema.TypeSet, - Required: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - utils.IDKey: { - Description: fmt.Sprintf( - "The resource identifier. It's equal to `%s`.", - ServiceAccountResourceClientIDKey, - ), - Type: schema.TypeString, - Computed: true, - }, - ServiceAccountResourceClientIDKey: { - Description: "The service account client ID.", - Type: schema.TypeString, - Computed: true, - }, - ServiceAccountResourceClientSecretKey: { - Description: "The service account client secret. **Note**: This resource is not able to recognize " + - "changes to the client secret after its creation, so keep in mind that if the client secret is " + - "rotated, the value present in this attribute will be outdated. If you need to rotate the client " + - "secret it's recommended that you recreate this terraform resource.", - Type: schema.TypeString, - Computed: true, - Sensitive: true, - }, - }, - } -} diff --git a/cyral/internal/serviceaccount/resource_cyral_service_account_test.go b/cyral/internal/serviceaccount/resource_test.go similarity index 100% rename from cyral/internal/serviceaccount/resource_cyral_service_account_test.go rename to cyral/internal/serviceaccount/resource_test.go diff --git a/cyral/internal/serviceaccount/schema_loader.go b/cyral/internal/serviceaccount/schema_loader.go new file mode 100644 index 00000000..4e4d694e --- /dev/null +++ b/cyral/internal/serviceaccount/schema_loader.go @@ -0,0 +1,26 @@ +package serviceaccount + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" +) + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "serviceaccount" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: resourceName, + Type: core.ResourceSchemaType, + Schema: resourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/sidecar/credentials/schema_loader.go b/cyral/internal/sidecar/credentials/schema_loader.go index 17a4fe9e..85516ad2 100644 --- a/cyral/internal/sidecar/credentials/schema_loader.go +++ b/cyral/internal/sidecar/credentials/schema_loader.go @@ -8,7 +8,7 @@ type packageSchema struct { } func (p *packageSchema) Name() string { - return "credentials" + return "sidecar.credentials" } func (p *packageSchema) Schemas() []*core.SchemaDescriptor { diff --git a/cyral/internal/sidecar/health/constants.go b/cyral/internal/sidecar/health/constants.go new file mode 100644 index 00000000..d5e653a8 --- /dev/null +++ b/cyral/internal/sidecar/health/constants.go @@ -0,0 +1,5 @@ +package health + +const ( + dataSourceName = "cyral_sidecar_health" +) diff --git a/cyral/internal/sidecar/health/data_source_cyral_sidecar_health.go b/cyral/internal/sidecar/health/datasource.go similarity index 80% rename from cyral/internal/sidecar/health/data_source_cyral_sidecar_health.go rename to cyral/internal/sidecar/health/datasource.go index ec27226c..8eb39948 100644 --- a/cyral/internal/sidecar/health/data_source_cyral_sidecar_health.go +++ b/cyral/internal/sidecar/health/datasource.go @@ -8,25 +8,13 @@ import ( "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/operationtype" "github.com/cyralinc/terraform-provider-cyral/cyral/utils" - "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -type SidecarHealth struct { - Status string `json:"status"` -} - -func (health *SidecarHealth) WriteToSchema(d *schema.ResourceData) error { - d.SetId(uuid.New().String()) - d.Set(utils.StatusKey, health.Status) - - return nil -} - -func DataSourceSidecarHealth() *schema.Resource { +func dataSourceSchema() *schema.Resource { return &schema.Resource{ Description: "Retrieve aggregated information about the " + - "[sidecar's health](https://cyral.com/docs/sidecars/sidecar-manage/#check-sidecar-cluster-status), " + + "[sidecar's health](https://cyral.com/docs/sidecars/manage/#check-sidecar-cluster-status), " + "considering all instances of the sidecar.", ReadContext: core.ReadResource(core.ResourceOperationConfig{ ResourceName: "SidecarHealthDataSourceRead", diff --git a/cyral/internal/sidecar/health/data_source_cyral_sidecar_health_test.go b/cyral/internal/sidecar/health/datasource_test.go similarity index 100% rename from cyral/internal/sidecar/health/data_source_cyral_sidecar_health_test.go rename to cyral/internal/sidecar/health/datasource_test.go diff --git a/cyral/internal/sidecar/health/model.go b/cyral/internal/sidecar/health/model.go new file mode 100644 index 00000000..ed7fcdce --- /dev/null +++ b/cyral/internal/sidecar/health/model.go @@ -0,0 +1,18 @@ +package health + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/utils" + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type SidecarHealth struct { + Status string `json:"status"` +} + +func (health *SidecarHealth) WriteToSchema(d *schema.ResourceData) error { + d.SetId(uuid.New().String()) + d.Set(utils.StatusKey, health.Status) + + return nil +} diff --git a/cyral/internal/sidecar/health/schema_loader.go b/cyral/internal/sidecar/health/schema_loader.go new file mode 100644 index 00000000..1379a931 --- /dev/null +++ b/cyral/internal/sidecar/health/schema_loader.go @@ -0,0 +1,26 @@ +package health + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" +) + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "sidecar.health" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: dataSourceName, + Type: core.DataSourceSchemaType, + Schema: dataSourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/sidecar/instance/constants.go b/cyral/internal/sidecar/instance/constants.go new file mode 100644 index 00000000..67c184f0 --- /dev/null +++ b/cyral/internal/sidecar/instance/constants.go @@ -0,0 +1,23 @@ +package instance + +const ( + dataSourceName = "cyral_sidecar_instance" +) + +const ( + // Schema keys + SidecarInstanceListKey = "instance_list" + MetadataKey = "metadata" + MonitoringKey = "monitoring" + VersionKey = "version" + DynamicVersionKey = "dynamic_version" + CapabilitiesKey = "capabilities" + StartTimestampKey = "start_timestamp" + LastRegistrationKey = "last_registration" + RecyclingKey = "recycling" + RecyclableKey = "recyclable" + ServicesKey = "services" + MetricsPortKey = "metrics_port" + ComponentsKey = "components" + ErrorKey = "error" +) diff --git a/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance.go b/cyral/internal/sidecar/instance/datasource.go similarity index 90% rename from cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance.go rename to cyral/internal/sidecar/instance/datasource.go index 66dae7c1..c6495310 100644 --- a/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance.go +++ b/cyral/internal/sidecar/instance/datasource.go @@ -11,27 +11,11 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -const ( - // Schema keys - SidecarInstanceListKey = "instance_list" - MetadataKey = "metadata" - MonitoringKey = "monitoring" - VersionKey = "version" - DynamicVersionKey = "dynamic_version" - CapabilitiesKey = "capabilities" - StartTimestampKey = "start_timestamp" - LastRegistrationKey = "last_registration" - RecyclingKey = "recycling" - RecyclableKey = "recyclable" - ServicesKey = "services" - MetricsPortKey = "metrics_port" - ComponentsKey = "components" - ErrorKey = "error" -) - -func DataSourceSidecarInstance() *schema.Resource { +func dataSourceSchema() *schema.Resource { return &schema.Resource{ Description: "Retrieve sidecar instances.", + // The DefaultContextHandler is NOT used here as this data source intentionally + // does not handle 404 errors, returning them to the user. ReadContext: core.ReadResource(core.ResourceOperationConfig{ ResourceName: "SidecarInstanceDataSourceRead", Type: operationtype.Read, diff --git a/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_test.go b/cyral/internal/sidecar/instance/datasource_test.go similarity index 100% rename from cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_test.go rename to cyral/internal/sidecar/instance/datasource_test.go diff --git a/cyral/internal/sidecar/instance/model_sidecar_instance.go b/cyral/internal/sidecar/instance/model.go similarity index 100% rename from cyral/internal/sidecar/instance/model_sidecar_instance.go rename to cyral/internal/sidecar/instance/model.go diff --git a/cyral/internal/sidecar/instance/schema_loader.go b/cyral/internal/sidecar/instance/schema_loader.go new file mode 100644 index 00000000..2158f672 --- /dev/null +++ b/cyral/internal/sidecar/instance/schema_loader.go @@ -0,0 +1,26 @@ +package instance + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" +) + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "sidecar.instance" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: dataSourceName, + Type: core.DataSourceSchemaType, + Schema: dataSourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/sidecar/instance/stats/constants.go b/cyral/internal/sidecar/instance/stats/constants.go new file mode 100644 index 00000000..4b26bd39 --- /dev/null +++ b/cyral/internal/sidecar/instance/stats/constants.go @@ -0,0 +1,12 @@ +package stats + +const ( + dataSourceName = "cyral_sidecar_instance_stats" +) + +const ( + // Schema keys + InstanceIDKey = "instance_id" + QueriesPerSecondKey = "queries_per_second" + ActiveConnectionsKey = "active_connections" +) diff --git a/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_stats.go b/cyral/internal/sidecar/instance/stats/datasource.go similarity index 76% rename from cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_stats.go rename to cyral/internal/sidecar/instance/stats/datasource.go index 48e75319..bb4f35b3 100644 --- a/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_stats.go +++ b/cyral/internal/sidecar/instance/stats/datasource.go @@ -1,4 +1,4 @@ -package instance +package stats import ( "fmt" @@ -11,30 +11,12 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -const ( - // Schema keys - InstanceIDKey = "instance_id" - QueriesPerSecondKey = "queries_per_second" - ActiveConnectionsKey = "active_connections" -) - -type SidecarInstanceStats struct { - QueriesPerSecond float32 `json:"queriesPerSecond"` - ActiveConnections uint32 `json:"activeConnections"` -} - -func (stats *SidecarInstanceStats) WriteToSchema(d *schema.ResourceData) error { - d.SetId(d.Get(InstanceIDKey).(string)) - d.Set(QueriesPerSecondKey, stats.QueriesPerSecond) - d.Set(ActiveConnectionsKey, stats.ActiveConnections) - - return nil -} - -func DataSourceSidecarInstanceStats() *schema.Resource { +func dataSourceSchema() *schema.Resource { return &schema.Resource{ Description: "Retrieve sidecar instance statistics. See also data source " + "[`cyral_sidecar_instance`](../data-sources/sidecar_instance.md).", + // The DefaultContextHandler is NOT used here as this data source intentionally + // does not handle 404 errors, returning them to the user. ReadContext: core.ReadResource(core.ResourceOperationConfig{ ResourceName: "SidecarInstanceStatsDataSourceRead", Type: operationtype.Read, diff --git a/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_stats_test.go b/cyral/internal/sidecar/instance/stats/datasource_test.go similarity index 97% rename from cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_stats_test.go rename to cyral/internal/sidecar/instance/stats/datasource_test.go index 377d9664..b8494030 100644 --- a/cyral/internal/sidecar/instance/data_source_cyral_sidecar_instance_stats_test.go +++ b/cyral/internal/sidecar/instance/stats/datasource_test.go @@ -1,11 +1,11 @@ -package instance_test +package stats_test import ( "fmt" "regexp" "testing" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/instance" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/instance/stats" "github.com/cyralinc/terraform-provider-cyral/cyral/provider" "github.com/cyralinc/terraform-provider-cyral/cyral/utils" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -46,7 +46,7 @@ func accTestStepSidecarInstanceStatsDataSource_EmptyInstanceID(dataSourceName st `, dataSourceName, "some-sidecar-id") return resource.TestStep{ Config: config, - ExpectError: regexp.MustCompile(fmt.Sprintf(`The argument "%s" is required`, instance.InstanceIDKey)), + ExpectError: regexp.MustCompile(fmt.Sprintf(`The argument "%s" is required`, stats.InstanceIDKey)), } } diff --git a/cyral/internal/sidecar/instance/stats/model.go b/cyral/internal/sidecar/instance/stats/model.go new file mode 100644 index 00000000..0768509e --- /dev/null +++ b/cyral/internal/sidecar/instance/stats/model.go @@ -0,0 +1,18 @@ +package stats + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type SidecarInstanceStats struct { + QueriesPerSecond float32 `json:"queriesPerSecond"` + ActiveConnections uint32 `json:"activeConnections"` +} + +func (stats *SidecarInstanceStats) WriteToSchema(d *schema.ResourceData) error { + d.SetId(d.Get(InstanceIDKey).(string)) + d.Set(QueriesPerSecondKey, stats.QueriesPerSecond) + d.Set(ActiveConnectionsKey, stats.ActiveConnections) + + return nil +} diff --git a/cyral/internal/sidecar/instance/stats/schema_loader.go b/cyral/internal/sidecar/instance/stats/schema_loader.go new file mode 100644 index 00000000..7bb9f384 --- /dev/null +++ b/cyral/internal/sidecar/instance/stats/schema_loader.go @@ -0,0 +1,26 @@ +package stats + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" +) + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "sidecar.instance.stats" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: dataSourceName, + Type: core.DataSourceSchemaType, + Schema: dataSourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/internal/sidecar/listener/datasource.go b/cyral/internal/sidecar/listener/datasource.go index dfdd4128..0e40a7ee 100644 --- a/cyral/internal/sidecar/listener/datasource.go +++ b/cyral/internal/sidecar/listener/datasource.go @@ -16,7 +16,7 @@ var dsContextHandler = core.DefaultContextHandler{ ResourceName: dataSourceName, ResourceType: resourcetype.DataSource, SchemaWriterFactoryGetMethod: func(_ *schema.ResourceData) core.SchemaWriter { return &ReadDataSourceSidecarListenerAPIResponse{} }, - GetPutDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { + ReadUpdateDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/sidecars/%s/listeners", c.ControlPlane, d.Get(utils.SidecarIDKey).(string)) }, } diff --git a/cyral/internal/sidecar/listener/resource.go b/cyral/internal/sidecar/listener/resource.go index cf101d82..4dc69f18 100644 --- a/cyral/internal/sidecar/listener/resource.go +++ b/cyral/internal/sidecar/listener/resource.go @@ -24,7 +24,7 @@ var resourceContextHandler = core.DefaultContextHandler{ c.ControlPlane, d.Get(utils.SidecarIDKey).(string)) }, - GetPutDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { + ReadUpdateDeleteURLFactory: func(d *schema.ResourceData, c *client.Client) string { return fmt.Sprintf("https://%s/v1/sidecars/%s/listeners/%s", c.ControlPlane, d.Get(utils.SidecarIDKey).(string), diff --git a/cyral/internal/sidecar/listener/schema_loader.go b/cyral/internal/sidecar/listener/schema_loader.go index 68594311..e7cdc33f 100644 --- a/cyral/internal/sidecar/listener/schema_loader.go +++ b/cyral/internal/sidecar/listener/schema_loader.go @@ -8,7 +8,7 @@ type packageSchema struct { } func (p *packageSchema) Name() string { - return "listener" + return "sidecar.listener" } func (p *packageSchema) Schemas() []*core.SchemaDescriptor { diff --git a/cyral/internal/systeminfo/constants.go b/cyral/internal/systeminfo/constants.go new file mode 100644 index 00000000..ea447733 --- /dev/null +++ b/cyral/internal/systeminfo/constants.go @@ -0,0 +1,11 @@ +package systeminfo + +const ( + dataSourceName = "cyral_system_info" +) + +const ( + // Schema keys + ControlPlaneVersionKey = "control_plane_version" + SidecarLatestVersionKey = "sidecar_latest_version" +) diff --git a/cyral/internal/systeminfo/data_source_cyral_system_info.go b/cyral/internal/systeminfo/datasource.go similarity index 69% rename from cyral/internal/systeminfo/data_source_cyral_system_info.go rename to cyral/internal/systeminfo/datasource.go index 41f1d69a..e009a97a 100644 --- a/cyral/internal/systeminfo/data_source_cyral_system_info.go +++ b/cyral/internal/systeminfo/datasource.go @@ -8,32 +8,14 @@ import ( "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/cyralinc/terraform-provider-cyral/cyral/core/types/operationtype" "github.com/cyralinc/terraform-provider-cyral/cyral/utils" - "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) -const ( - // Schema keys - ControlPlaneVersionKey = "control_plane_version" - SidecarLatestVersionKey = "sidecar_latest_version" -) - -type SystemInfo struct { - ControlPlaneVersion string `json:"controlPlaneVersion"` - SidecarLatestVersion string `json:"sidecarLatestVersion"` -} - -func (systemInfo *SystemInfo) WriteToSchema(d *schema.ResourceData) error { - d.SetId(uuid.New().String()) - d.Set(ControlPlaneVersionKey, systemInfo.ControlPlaneVersion) - d.Set(SidecarLatestVersionKey, systemInfo.SidecarLatestVersion) - - return nil -} - -func DataSourceSystemInfo() *schema.Resource { +func dataSourceSchema() *schema.Resource { return &schema.Resource{ Description: "Retrieve information from Cyral system.", + // The DefaultContextHandler is NOT used here as this data source intentionally + // does not handle 404 errors, returning them to the user. ReadContext: core.ReadResource(core.ResourceOperationConfig{ ResourceName: "SystemInfoDataSourceRead", Type: operationtype.Read, diff --git a/cyral/internal/systeminfo/data_source_cyral_system_info_test.go b/cyral/internal/systeminfo/datasource_test.go similarity index 100% rename from cyral/internal/systeminfo/data_source_cyral_system_info_test.go rename to cyral/internal/systeminfo/datasource_test.go diff --git a/cyral/internal/systeminfo/model.go b/cyral/internal/systeminfo/model.go new file mode 100644 index 00000000..aed73587 --- /dev/null +++ b/cyral/internal/systeminfo/model.go @@ -0,0 +1,19 @@ +package systeminfo + +import ( + "github.com/google/uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +type SystemInfo struct { + ControlPlaneVersion string `json:"controlPlaneVersion"` + SidecarLatestVersion string `json:"sidecarLatestVersion"` +} + +func (systemInfo *SystemInfo) WriteToSchema(d *schema.ResourceData) error { + d.SetId(uuid.New().String()) + d.Set(ControlPlaneVersionKey, systemInfo.ControlPlaneVersion) + d.Set(SidecarLatestVersionKey, systemInfo.SidecarLatestVersion) + + return nil +} diff --git a/cyral/internal/systeminfo/schema_loader.go b/cyral/internal/systeminfo/schema_loader.go new file mode 100644 index 00000000..f43d27f4 --- /dev/null +++ b/cyral/internal/systeminfo/schema_loader.go @@ -0,0 +1,26 @@ +package systeminfo + +import ( + "github.com/cyralinc/terraform-provider-cyral/cyral/core" +) + +type packageSchema struct { +} + +func (p *packageSchema) Name() string { + return "systeminfo" +} + +func (p *packageSchema) Schemas() []*core.SchemaDescriptor { + return []*core.SchemaDescriptor{ + { + Name: dataSourceName, + Type: core.DataSourceSchemaType, + Schema: dataSourceSchema, + }, + } +} + +func PackageSchema() core.PackageSchema { + return &packageSchema{} +} diff --git a/cyral/provider/provider.go b/cyral/provider/provider.go index 609efb86..7d174974 100644 --- a/cyral/provider/provider.go +++ b/cyral/provider/provider.go @@ -12,22 +12,7 @@ import ( "github.com/cyralinc/terraform-provider-cyral/cyral/client" "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/cyralinc/terraform-provider-cyral/cyral/internal/deprecated" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/awsiam" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/confextension/mfaduo" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/confextension/pagerduty" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/idpsaml" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/logging" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/permission" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/policy" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/policy/rule" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/regopolicy" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/role" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/samlconfiguration" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/serviceaccount" "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/health" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/instance" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/systeminfo" ) func init() { @@ -87,7 +72,7 @@ func Provider() *schema.Provider { func getDataSourceMap(ps []core.PackageSchema) map[string]*schema.Resource { ctx := context.Background() - tflog.Debug(ctx, fmt.Sprintf("Init getDataSourceMap")) + tflog.Debug(ctx, "Init getDataSourceMap") schemaMap := map[string]*schema.Resource{} for _, p := range ps { tflog.Debug(ctx, fmt.Sprintf("Looking for datasources in package `%s`", p.Name())) @@ -100,19 +85,12 @@ func getDataSourceMap(ps []core.PackageSchema) map[string]*schema.Resource { } schemaMap["cyral_integration_idp"] = deprecated.DataSourceIntegrationIdP() - schemaMap["cyral_integration_idp_saml"] = idpsaml.DataSourceIntegrationIdPSAML() - schemaMap["cyral_integration_logging"] = logging.DataSourceIntegrationLogging() - schemaMap["cyral_permission"] = permission.DataSourcePermission() - schemaMap["cyral_role"] = role.DataSourceRole() - schemaMap["cyral_saml_configuration"] = samlconfiguration.DataSourceSAMLConfiguration() - schemaMap["cyral_sidecar_bound_ports"] = sidecar.DataSourceSidecarBoundPorts() + schemaMap["cyral_saml_configuration"] = deprecated.DataSourceSAMLConfiguration() schemaMap["cyral_sidecar_cft_template"] = deprecated.DataSourceSidecarCftTemplate() - schemaMap["cyral_sidecar_health"] = health.DataSourceSidecarHealth() - schemaMap["cyral_sidecar_id"] = sidecar.DataSourceSidecarID() schemaMap["cyral_sidecar_instance_ids"] = deprecated.DataSourceSidecarInstanceIDs() - schemaMap["cyral_sidecar_instance_stats"] = instance.DataSourceSidecarInstanceStats() - schemaMap["cyral_sidecar_instance"] = instance.DataSourceSidecarInstance() - schemaMap["cyral_system_info"] = systeminfo.DataSourceSystemInfo() + + schemaMap["cyral_sidecar_bound_ports"] = sidecar.DataSourceSidecarBoundPorts() + schemaMap["cyral_sidecar_id"] = sidecar.DataSourceSidecarID() tflog.Debug(ctx, "End getDataSourceMap") @@ -134,15 +112,11 @@ func getResourceMap(ps []core.PackageSchema) map[string]*schema.Resource { } } - // // TODO Once the resources are migrated to the new SchemaRegister - // // abstraction, these calls from provider to resource will be removed. - schemaMap["cyral_integration_aws_iam"] = awsiam.ResourceIntegrationAWSIAM() + // TODO Remove all the following resources in the next major version. schemaMap["cyral_integration_datadog"] = deprecated.ResourceIntegrationDatadog() - schemaMap["cyral_integration_mfa_duo"] = mfaduo.ResourceIntegrationMFADuo() schemaMap["cyral_integration_elk"] = deprecated.ResourceIntegrationELK() schemaMap["cyral_integration_logstash"] = deprecated.ResourceIntegrationLogstash() schemaMap["cyral_integration_looker"] = deprecated.ResourceIntegrationLooker() - schemaMap["cyral_integration_pager_duty"] = pagerduty.ResourceIntegrationPagerDuty() schemaMap["cyral_integration_splunk"] = deprecated.ResourceIntegrationSplunk() schemaMap["cyral_integration_idp_aad"] = deprecated.ResourceIntegrationIdP("aad", idpDeprecationMessage) schemaMap["cyral_integration_idp_adfs"] = deprecated.ResourceIntegrationIdP("adfs-2016", idpDeprecationMessage) @@ -150,16 +124,7 @@ func getResourceMap(ps []core.PackageSchema) map[string]*schema.Resource { schemaMap["cyral_integration_idp_gsuite"] = deprecated.ResourceIntegrationIdP("gsuite", idpDeprecationMessage) schemaMap["cyral_integration_idp_okta"] = deprecated.ResourceIntegrationIdP("okta", idpDeprecationMessage) schemaMap["cyral_integration_idp_ping_one"] = deprecated.ResourceIntegrationIdP("pingone", idpDeprecationMessage) - schemaMap["cyral_integration_idp_saml"] = idpsaml.ResourceIntegrationIdPSAML() - schemaMap["cyral_integration_idp_saml_draft"] = idpsaml.ResourceIntegrationIdPSAMLDraft() schemaMap["cyral_integration_sumo_logic"] = deprecated.ResourceIntegrationSumoLogic() - schemaMap["cyral_integration_logging"] = logging.ResourceIntegrationLogging() - schemaMap["cyral_policy"] = policy.ResourcePolicy() - schemaMap["cyral_policy_rule"] = rule.ResourcePolicyRule() - schemaMap["cyral_rego_policy_instance"] = regopolicy.ResourceRegoPolicyInstance() - schemaMap["cyral_role"] = role.ResourceRole() - schemaMap["cyral_role_sso_groups"] = role.ResourceRoleSSOGroups() - schemaMap["cyral_service_account"] = serviceaccount.ResourceServiceAccount() tflog.Debug(ctx, "End getResourceMap") diff --git a/cyral/provider/schema_loader.go b/cyral/provider/schema_loader.go index 4cc5f372..b474fe1f 100644 --- a/cyral/provider/schema_loader.go +++ b/cyral/provider/schema_loader.go @@ -3,45 +3,79 @@ package provider import ( "github.com/cyralinc/terraform-provider-cyral/cyral/core" "github.com/cyralinc/terraform-provider-cyral/cyral/internal/datalabel" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/hcvault" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/slack" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/teams" + integration_awsiam "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/awsiam" + integration_mfa_duo "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/confextension/mfaduo" + integration_pager_duty "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/confextension/pagerduty" + integration_hcvault "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/hcvault" + integration_idp_saml "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/idpsaml" + integration_idp_saml_draft "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/idpsaml/draft" + integration_logging "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/logging" + integration_slack "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/slack" + integration_teams "github.com/cyralinc/terraform-provider-cyral/cyral/internal/integration/teams" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/permission" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/policy" + policy_rule "github.com/cyralinc/terraform-provider-cyral/cyral/internal/policy/rule" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/regopolicy" "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/accessgateway" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/accessrules" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/binding" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/confanalysis" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/confauth" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/datamap" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/network" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/useraccount" + repository_accessgateway "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/accessgateway" + repository_accessrules "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/accessrules" + repository_binding "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/binding" + repository_confanalysis "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/confanalysis" + repository_confauth "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/confauth" + repository_datamap "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/datamap" + repository_network "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/network" + repository_useraccount "github.com/cyralinc/terraform-provider-cyral/cyral/internal/repository/useraccount" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/role" + role_ssogroups "github.com/cyralinc/terraform-provider-cyral/cyral/internal/role/ssogroups" "github.com/cyralinc/terraform-provider-cyral/cyral/internal/samlcertificate" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/serviceaccount" "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/credentials" - "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/listener" + sidecar_credentials "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/credentials" + sidecar_health "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/health" + sidecar_instance "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/instance" + sidecar_instance_stats "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/instance/stats" + sidecar_listener "github.com/cyralinc/terraform-provider-cyral/cyral/internal/sidecar/listener" + "github.com/cyralinc/terraform-provider-cyral/cyral/internal/systeminfo" "github.com/cyralinc/terraform-provider-cyral/cyral/internal/tokensettings" ) func packagesSchemas() []core.PackageSchema { v := []core.PackageSchema{ - accessgateway.PackageSchema(), - accessrules.PackageSchema(), - binding.PackageSchema(), - confanalysis.PackageSchema(), - confauth.PackageSchema(), - credentials.PackageSchema(), datalabel.PackageSchema(), - datamap.PackageSchema(), - hcvault.PackageSchema(), - listener.PackageSchema(), - network.PackageSchema(), + integration_awsiam.PackageSchema(), + integration_hcvault.PackageSchema(), + integration_idp_saml.PackageSchema(), + integration_idp_saml_draft.PackageSchema(), + integration_logging.PackageSchema(), + integration_mfa_duo.PackageSchema(), + integration_pager_duty.PackageSchema(), + integration_slack.PackageSchema(), + integration_teams.PackageSchema(), + permission.PackageSchema(), + policy.PackageSchema(), + policy_rule.PackageSchema(), + regopolicy.PackageSchema(), repository.PackageSchema(), + repository_accessgateway.PackageSchema(), + repository_accessrules.PackageSchema(), + repository_binding.PackageSchema(), + repository_confanalysis.PackageSchema(), + repository_confauth.PackageSchema(), + repository_datamap.PackageSchema(), + repository_network.PackageSchema(), + repository_useraccount.PackageSchema(), + role.PackageSchema(), + role_ssogroups.PackageSchema(), samlcertificate.PackageSchema(), + serviceaccount.PackageSchema(), sidecar.PackageSchema(), - slack.PackageSchema(), - teams.PackageSchema(), + sidecar_credentials.PackageSchema(), + sidecar_health.PackageSchema(), + sidecar_listener.PackageSchema(), + sidecar_instance.PackageSchema(), + sidecar_instance_stats.PackageSchema(), + systeminfo.PackageSchema(), tokensettings.PackageSchema(), - useraccount.PackageSchema(), } return v } diff --git a/docs/data-sources/saml_configuration.md b/docs/data-sources/saml_configuration.md index 0c390a82..38395408 100644 --- a/docs/data-sources/saml_configuration.md +++ b/docs/data-sources/saml_configuration.md @@ -3,15 +3,12 @@ page_title: "cyral_saml_configuration Data Source - terraform-provider-cyral" subcategory: "" description: |- - Parses a SAML metadata URL or a Base64 document into a SAML configuration. - See also the remaining SAML-related resources and data sources. + ~> DEPRECATED This data source has been deprecated. It will be removed in the next major version of the provider. --- # cyral_saml_configuration (Data Source) -Parses a SAML metadata URL or a Base64 document into a SAML configuration. - -See also the remaining SAML-related resources and data sources. +~> **DEPRECATED** This data source has been deprecated. It will be removed in the next major version of the provider. ## Example Usage diff --git a/docs/data-sources/sidecar_cft_template.md b/docs/data-sources/sidecar_cft_template.md index a75424be..60f7b841 100644 --- a/docs/data-sources/sidecar_cft_template.md +++ b/docs/data-sources/sidecar_cft_template.md @@ -1,6 +1,6 @@ # cyral_sidecar_cft_template (Data Source) -~> **DEPRECATED** This data source was deprecated. It will be removed in the next major version of the provider and no longer works for control planes `v4.13` and later. +~> **DEPRECATED** This data source has been deprecated. It will be removed in the next major version of the provider and no longer works for control planes `v4.13` and later. ## Example Usage diff --git a/docs/data-sources/sidecar_health.md b/docs/data-sources/sidecar_health.md index 4534d76f..a08f4d11 100644 --- a/docs/data-sources/sidecar_health.md +++ b/docs/data-sources/sidecar_health.md @@ -3,12 +3,12 @@ page_title: "cyral_sidecar_health Data Source - terraform-provider-cyral" subcategory: "" description: |- - Retrieve aggregated information about the sidecar's health https://cyral.com/docs/sidecars/sidecar-manage/#check-sidecar-cluster-status, considering all instances of the sidecar. + Retrieve aggregated information about the sidecar's health https://cyral.com/docs/sidecars/manage/#check-sidecar-cluster-status, considering all instances of the sidecar. --- # cyral_sidecar_health (Data Source) -Retrieve aggregated information about the [sidecar's health](https://cyral.com/docs/sidecars/sidecar-manage/#check-sidecar-cluster-status), considering all instances of the sidecar. +Retrieve aggregated information about the [sidecar's health](https://cyral.com/docs/sidecars/manage/#check-sidecar-cluster-status), considering all instances of the sidecar. diff --git a/docs/data-sources/sidecar_instance_ids.md b/docs/data-sources/sidecar_instance_ids.md index 78ff0e59..1e0ddc8d 100644 --- a/docs/data-sources/sidecar_instance_ids.md +++ b/docs/data-sources/sidecar_instance_ids.md @@ -3,12 +3,12 @@ page_title: "cyral_sidecar_instance_ids Data Source - terraform-provider-cyral" subcategory: "" description: |- - ~> DEPRECATED This data source was deprecated. It will be removed in the next major version of the provider. Use the data source cyral_sidecar_instance instead + ~> DEPRECATED This data source has been deprecated. It will be removed in the next major version of the provider. Use the data source cyral_sidecar_instance instead --- # cyral_sidecar_instance_ids (Data Source) -~> **DEPRECATED** This data source was deprecated. It will be removed in the next major version of the provider. Use the data source `cyral_sidecar_instance` instead +~> **DEPRECATED** This data source has been deprecated. It will be removed in the next major version of the provider. Use the data source `cyral_sidecar_instance` instead ## Example Usage