From 159a089c535e2280026a341608f316aadbf29bf8 Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Wed, 28 Feb 2024 12:04:58 +0100 Subject: [PATCH 01/15] Support restriction in elasticstack_elasticsearch_security_api_key --- internal/elasticsearch/security/api_key.go | 2 +- .../elasticsearch/security/api_key_test.go | 86 +++++++++++++++++-- internal/models/models.go | 35 +++++--- 3 files changed, 107 insertions(+), 16 deletions(-) diff --git a/internal/elasticsearch/security/api_key.go b/internal/elasticsearch/security/api_key.go index ef44aa1ad..51ac08290 100644 --- a/internal/elasticsearch/security/api_key.go +++ b/internal/elasticsearch/security/api_key.go @@ -107,7 +107,7 @@ func resourceSecurityApiKeyCreate(ctx context.Context, d *schema.ResourceData, m } if v, ok := d.GetOk("role_descriptors"); ok { - role_descriptors := map[string]models.Role{} + role_descriptors := map[string]models.ApiKeyRoleDescriptor{} if err := json.NewDecoder(strings.NewReader(v.(string))).Decode(&role_descriptors); err != nil { return diag.FromErr(err) } diff --git a/internal/elasticsearch/security/api_key_test.go b/internal/elasticsearch/security/api_key_test.go index 5bac86911..b973a682f 100644 --- a/internal/elasticsearch/security/api_key_test.go +++ b/internal/elasticsearch/security/api_key_test.go @@ -17,7 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) -func TestAccResourceSecuritApiKey(t *testing.T) { +func TestAccResourceSecurityApiKey(t *testing.T) { // generate a random name apiKeyName := sdkacctest.RandStringFromCharSet(10, sdkacctest.CharSetAlphaNum) @@ -28,17 +28,17 @@ func TestAccResourceSecuritApiKey(t *testing.T) { Steps: []resource.TestStep{ { SkipFunc: versionutils.CheckIfVersionIsUnsupported(security.APIKeyMinVersion), - Config: testAccResourceSecuritApiKeyCreate(apiKeyName), + Config: testAccResourceSecurityApiKeyCreate(apiKeyName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_api_key.test", "name", apiKeyName), resource.TestCheckResourceAttrWith("elasticstack_elasticsearch_security_api_key.test", "role_descriptors", func(testValue string) error { - var testRoleDescriptor map[string]models.Role + var testRoleDescriptor map[string]models.ApiKeyRoleDescriptor if err := json.Unmarshal([]byte(testValue), &testRoleDescriptor); err != nil { return err } allowRestrictedIndices := false - expectedRoleDescriptor := map[string]models.Role{ + expectedRoleDescriptor := map[string]models.ApiKeyRoleDescriptor{ "role-a": { Cluster: []string{"all"}, Indices: []models.IndexPerms{{ @@ -64,7 +64,55 @@ func TestAccResourceSecuritApiKey(t *testing.T) { }) } -func testAccResourceSecuritApiKeyCreate(apiKeyName string) string { +func TestAccResourceSecurityApiKeyWithWorkflowRestriction(t *testing.T) { + // generate a random name + apiKeyName := sdkacctest.RandStringFromCharSet(10, sdkacctest.CharSetAlphaNum) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + CheckDestroy: checkResourceSecurityApiKeyDestroy, + ProtoV6ProviderFactories: acctest.Providers, + Steps: []resource.TestStep{ + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(security.APIKeyMinVersion), + Config: testAccResourceSecurityApiKeyCreateWithWorkflowRestriction(apiKeyName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_api_key.test", "name", apiKeyName), + resource.TestCheckResourceAttrWith("elasticstack_elasticsearch_security_api_key.test", "role_descriptors", func(testValue string) error { + var testRoleDescriptor map[string]models.ApiKeyRoleDescriptor + if err := json.Unmarshal([]byte(testValue), &testRoleDescriptor); err != nil { + return err + } + + allowRestrictedIndices := false + expectedRoleDescriptor := map[string]models.ApiKeyRoleDescriptor{ + "role-a": { + Cluster: []string{"all"}, + Indices: []models.IndexPerms{{ + Names: []string{"index-a*"}, + Privileges: []string{"read"}, + AllowRestrictedIndices: &allowRestrictedIndices, + }}, + Restriction: &models.Restriction{Workflows: []string{"search_application_query"}}, + }, + } + + if !reflect.DeepEqual(testRoleDescriptor, expectedRoleDescriptor) { + return fmt.Errorf("%v doesn't match %v", testRoleDescriptor, expectedRoleDescriptor) + } + + return nil + }), + resource.TestCheckResourceAttrSet("elasticstack_elasticsearch_security_api_key.test", "expiration"), + resource.TestCheckResourceAttrSet("elasticstack_elasticsearch_security_api_key.test", "api_key"), + resource.TestCheckResourceAttrSet("elasticstack_elasticsearch_security_api_key.test", "encoded"), + ), + }, + }, + }) +} + +func testAccResourceSecurityApiKeyCreate(apiKeyName string) string { return fmt.Sprintf(` provider "elasticstack" { elasticsearch {} @@ -89,6 +137,34 @@ resource "elasticstack_elasticsearch_security_api_key" "test" { `, apiKeyName) } +func testAccResourceSecurityApiKeyCreateWithWorkflowRestriction(apiKeyName string) string { + return fmt.Sprintf(` +provider "elasticstack" { + elasticsearch {} +} + +resource "elasticstack_elasticsearch_security_api_key" "test" { + name = "%s" + + role_descriptors = jsonencode({ + role-a = { + cluster = ["all"] + indices = [{ + names = ["index-a*"] + privileges = ["read"] + allow_restricted_indices = false + }], + restriction = { + workflows = [ "search_application_query"] + } + } + }) + + expiration = "1d" +} + `, apiKeyName) +} + func checkResourceSecurityApiKeyDestroy(s *terraform.State) error { client, err := clients.NewAcceptanceTestingClient() if err != nil { diff --git a/internal/models/models.go b/internal/models/models.go index 4f4918a14..dc8af8937 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -82,6 +82,21 @@ type Role struct { RusAs []string `json:"run_as,omitempty"` } +type ApiKeyRoleDescriptor struct { + Name string `json:"-"` + Applications []Application `json:"applications,omitempty"` + Global map[string]interface{} `json:"global,omitempty"` + Cluster []string `json:"cluster,omitempty"` + Indices []IndexPerms `json:"indices,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` + RusAs []string `json:"run_as,omitempty"` + Restriction *Restriction `json:"restriction,omitempty"` +} + +type Restriction struct { + Workflows []string `json:"workflows,omitempty"` +} + type RoleMapping struct { Name string `json:"-"` Enabled bool `json:"enabled"` @@ -92,20 +107,20 @@ type RoleMapping struct { } type ApiKey struct { - Name string `json:"name"` - RolesDescriptors map[string]Role `json:"role_descriptors,omitempty"` - Expiration string `json:"expiration,omitempty"` - Metadata map[string]interface{} `json:"metadata,omitempty"` + Name string `json:"name"` + RolesDescriptors map[string]ApiKeyRoleDescriptor `json:"role_descriptors,omitempty"` + Expiration string `json:"expiration,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` } type ApiKeyResponse struct { ApiKey - RolesDescriptors map[string]Role `json:"role_descriptors,omitempty"` - Expiration int64 `json:"expiration,omitempty"` - Id string `json:"id,omitempty"` - Key string `json:"api_key,omitempty"` - EncodedKey string `json:"encoded,omitempty"` - Invalidated bool `json:"invalidated,omitempty"` + RolesDescriptors map[string]ApiKeyRoleDescriptor `json:"role_descriptors,omitempty"` + Expiration int64 `json:"expiration,omitempty"` + Id string `json:"id,omitempty"` + Key string `json:"api_key,omitempty"` + EncodedKey string `json:"encoded,omitempty"` + Invalidated bool `json:"invalidated,omitempty"` } type IndexPerms struct { From 7e421f522cdb4bf0cda06995e20e5862b733598f Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Thu, 29 Feb 2024 13:33:13 +0100 Subject: [PATCH 02/15] Update example in docs --- .../elasticsearch_security_api_key.md | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/resources/elasticsearch_security_api_key.md b/docs/resources/elasticsearch_security_api_key.md index 9d9c9b90e..dbff69c74 100644 --- a/docs/resources/elasticsearch_security_api_key.md +++ b/docs/resources/elasticsearch_security_api_key.md @@ -41,6 +41,37 @@ resource "elasticstack_elasticsearch_security_api_key" "api_key" { }) } +resource "elasticstack_elasticsearch_security_api_key" "api_key_with_restriction" { + # Set the name + name = "My API key" + + # Set the role descriptors + role_descriptors = jsonencode({ + role-a = { + cluster = ["all"], + indices = [ + { + names = ["index-a*"], + privileges = ["read"] + } + ], + restriction = { + workflows = [ "search_application_query" ] + } + } + }) + + # Set the expiration for the API key + expiration = "1d" + + # Set the custom metadata for this user + metadata = jsonencode({ + "env" = "testing" + "open" = false + "number" = 49 + }) +} + output "api_key" { value = elasticstack_elasticsearch_security_api_key.api_key sensitive = true From 60c5b1557278f8812487b3d0020f637d1ab6c3d9 Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Thu, 29 Feb 2024 13:52:04 +0100 Subject: [PATCH 03/15] Skip test for API key with restriction on any version below 8.9 --- internal/elasticsearch/security/api_key.go | 3 ++- internal/elasticsearch/security/api_key_test.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/elasticsearch/security/api_key.go b/internal/elasticsearch/security/api_key.go index 51ac08290..a0cdb6d5f 100644 --- a/internal/elasticsearch/security/api_key.go +++ b/internal/elasticsearch/security/api_key.go @@ -16,7 +16,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) -var APIKeyMinVersion = version.Must(version.NewVersion("8.0.0")) // Enabled in 8.0 +var APIKeyMinVersion = version.Must(version.NewVersion("8.0.0")) // Enabled in 8.0 +var APIKeyWithRestrictionMinVersion = version.Must(version.NewVersion("8.9.0")) // Enabled in 8.0 func ResourceApiKey() *schema.Resource { apikeySchema := map[string]*schema.Schema{ diff --git a/internal/elasticsearch/security/api_key_test.go b/internal/elasticsearch/security/api_key_test.go index b973a682f..ee81b5783 100644 --- a/internal/elasticsearch/security/api_key_test.go +++ b/internal/elasticsearch/security/api_key_test.go @@ -74,7 +74,7 @@ func TestAccResourceSecurityApiKeyWithWorkflowRestriction(t *testing.T) { ProtoV6ProviderFactories: acctest.Providers, Steps: []resource.TestStep{ { - SkipFunc: versionutils.CheckIfVersionIsUnsupported(security.APIKeyMinVersion), + SkipFunc: versionutils.CheckIfVersionIsUnsupported(security.APIKeyWithRestrictionMinVersion), Config: testAccResourceSecurityApiKeyCreateWithWorkflowRestriction(apiKeyName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("elasticstack_elasticsearch_security_api_key.test", "name", apiKeyName), From b74def40723aed38b8304914177ae92c254853fe Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Thu, 29 Feb 2024 14:07:10 +0100 Subject: [PATCH 04/15] Update docs --- .../elasticsearch_security_api_key.md | 2 +- .../resource.tf | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/docs/resources/elasticsearch_security_api_key.md b/docs/resources/elasticsearch_security_api_key.md index dbff69c74..47e9e147d 100644 --- a/docs/resources/elasticsearch_security_api_key.md +++ b/docs/resources/elasticsearch_security_api_key.md @@ -41,10 +41,10 @@ resource "elasticstack_elasticsearch_security_api_key" "api_key" { }) } +# restriction on a role descriptor for an API key is supported since Elastic 8.9 resource "elasticstack_elasticsearch_security_api_key" "api_key_with_restriction" { # Set the name name = "My API key" - # Set the role descriptors role_descriptors = jsonencode({ role-a = { diff --git a/examples/resources/elasticstack_elasticsearch_security_api_key/resource.tf b/examples/resources/elasticstack_elasticsearch_security_api_key/resource.tf index 9ddad52ec..97f02bbc2 100644 --- a/examples/resources/elasticstack_elasticsearch_security_api_key/resource.tf +++ b/examples/resources/elasticstack_elasticsearch_security_api_key/resource.tf @@ -26,6 +26,37 @@ resource "elasticstack_elasticsearch_security_api_key" "api_key" { }) } +# restriction on a role descriptor for an API key is supported since Elastic 8.9 +resource "elasticstack_elasticsearch_security_api_key" "api_key_with_restriction" { + # Set the name + name = "My API key" + # Set the role descriptors + role_descriptors = jsonencode({ + role-a = { + cluster = ["all"], + indices = [ + { + names = ["index-a*"], + privileges = ["read"] + } + ], + restriction = { + workflows = [ "search_application_query" ] + } + } + }) + + # Set the expiration for the API key + expiration = "1d" + + # Set the custom metadata for this user + metadata = jsonencode({ + "env" = "testing" + "open" = false + "number" = 49 + }) +} + output "api_key" { value = elasticstack_elasticsearch_security_api_key.api_key sensitive = true From ef528c66fb36d247f7d5c9df25ba7ceb18dd95a2 Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Thu, 29 Feb 2024 14:38:13 +0100 Subject: [PATCH 05/15] Raise error when restriction is not supported for API keys --- internal/elasticsearch/security/api_key.go | 50 +++++++++++++++++++ .../elasticsearch/security/api_key_test.go | 36 +++++++++++++ 2 files changed, 86 insertions(+) diff --git a/internal/elasticsearch/security/api_key.go b/internal/elasticsearch/security/api_key.go index a0cdb6d5f..e4575a62c 100644 --- a/internal/elasticsearch/security/api_key.go +++ b/internal/elasticsearch/security/api_key.go @@ -3,6 +3,7 @@ package security import ( "context" "encoding/json" + "github.com/elastic/go-elasticsearch/v7/esapi" "regexp" "strings" @@ -92,6 +93,16 @@ func ResourceApiKey() *schema.Resource { } } +type esVersion struct { + Number string `json:"number"` + BuildFlavor string `json:"build_flavor"` +} + +type info struct { + Version esVersion `json:"version"` + Tagline string `json:"tagline"` +} + func resourceSecurityApiKeyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client, diags := clients.NewApiClientFromSDKResource(d, meta) if diags.HasError() { @@ -113,6 +124,45 @@ func resourceSecurityApiKeyCreate(ctx context.Context, d *schema.ResourceData, m return diag.FromErr(err) } apikey.RolesDescriptors = role_descriptors + + var hasRestriction = false + var keysWithRestrictions = []string{} + + for key, roleDescriptor := range role_descriptors { + if roleDescriptor.Restriction != nil { + hasRestriction = true + keysWithRestrictions = append(keysWithRestrictions, key) + } + } + + if hasRestriction { + if esClient, err := client.GetESClient(); err == nil { + req := esapi.InfoRequest{} + res, err := req.Do(ctx, esClient.Transport) + if err != nil { + return diag.FromErr(err) + } + + defer res.Body.Close() + + var info info + err = json.NewDecoder(res.Body).Decode(&info) + if err != nil { + return diag.Errorf("error decoding Elasticsearch informations: %s", err) + } + + currentVersion, err := version.NewVersion(info.Version.Number) + if err != nil { + return diag.FromErr(err) + } + + supportedVersion, err := version.NewVersion("8.9.0") + + if currentVersion.LessThan(supportedVersion) && hasRestriction { + return diag.Errorf(`Specifying "restriction" on an API key role description is not supported in this version of Elasticsearch. API keys: %s, role descriptor(s) %s`, apikey.Name, strings.Join(keysWithRestrictions, ", ")) + } + } + } } if v, ok := d.GetOk("metadata"); ok { diff --git a/internal/elasticsearch/security/api_key_test.go b/internal/elasticsearch/security/api_key_test.go index ee81b5783..8521f2ebe 100644 --- a/internal/elasticsearch/security/api_key_test.go +++ b/internal/elasticsearch/security/api_key_test.go @@ -1,9 +1,12 @@ package security_test import ( + "context" "encoding/json" "fmt" + "github.com/hashicorp/go-version" "reflect" + "regexp" "testing" "github.com/elastic/terraform-provider-elasticstack/internal/acctest" @@ -112,6 +115,39 @@ func TestAccResourceSecurityApiKeyWithWorkflowRestriction(t *testing.T) { }) } +func TestAccResourceSecurityApiKeyWithWorkflowRestrictionOnElasticPre8_9_x(t *testing.T) { + // generate a random name + apiKeyName := sdkacctest.RandStringFromCharSet(10, sdkacctest.CharSetAlphaNum) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + CheckDestroy: checkResourceSecurityApiKeyDestroy, + ProtoV6ProviderFactories: acctest.Providers, + Steps: []resource.TestStep{ + { + SkipFunc: SkipWhenApiKeysAreNotSupportedOrRestrictionsAreSupported(security.APIKeyMinVersion, security.APIKeyWithRestrictionMinVersion), + Config: testAccResourceSecurityApiKeyCreateWithWorkflowRestriction(apiKeyName), + ExpectError: regexp.MustCompile(fmt.Sprintf(`Specifying "restriction" on an API key role description is not supported in this version of Elasticsearch. API keys: %s, role descriptor(s) %s`, apiKeyName, "role-a")), + }, + }, + }) +} + +func SkipWhenApiKeysAreNotSupportedOrRestrictionsAreSupported(minApiKeySupportedVersion *version.Version, minRestrictionSupportedVersion *version.Version) func() (bool, error) { + return func() (b bool, err error) { + client, err := clients.NewAcceptanceTestingClient() + if err != nil { + return false, err + } + serverVersion, diags := client.ServerVersion(context.Background()) + if diags.HasError() { + return false, fmt.Errorf("failed to parse the elasticsearch version %v", diags) + } + + return serverVersion.LessThan(minApiKeySupportedVersion) || serverVersion.GreaterThanOrEqual(minRestrictionSupportedVersion), nil + } +} + func testAccResourceSecurityApiKeyCreate(apiKeyName string) string { return fmt.Sprintf(` provider "elasticstack" { From f049ea0e22da0b4b00003482cf55086381736674 Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Thu, 29 Feb 2024 14:40:24 +0100 Subject: [PATCH 06/15] Appease the linter --- internal/elasticsearch/security/api_key.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/elasticsearch/security/api_key.go b/internal/elasticsearch/security/api_key.go index e4575a62c..88981edf4 100644 --- a/internal/elasticsearch/security/api_key.go +++ b/internal/elasticsearch/security/api_key.go @@ -156,8 +156,8 @@ func resourceSecurityApiKeyCreate(ctx context.Context, d *schema.ResourceData, m return diag.FromErr(err) } - supportedVersion, err := version.NewVersion("8.9.0") - + supportedVersion, _ := version.NewVersion("8.9.0") + if currentVersion.LessThan(supportedVersion) && hasRestriction { return diag.Errorf(`Specifying "restriction" on an API key role description is not supported in this version of Elasticsearch. API keys: %s, role descriptor(s) %s`, apikey.Name, strings.Join(keysWithRestrictions, ", ")) } From 433ab4042b28d5b370ac738d7e776c8f7cc90dc8 Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Thu, 29 Feb 2024 14:57:11 +0100 Subject: [PATCH 07/15] Small refactor --- internal/elasticsearch/security/api_key.go | 63 +++++++++++-------- .../elasticsearch/security/api_key_test.go | 7 ++- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/internal/elasticsearch/security/api_key.go b/internal/elasticsearch/security/api_key.go index 88981edf4..b449e8761 100644 --- a/internal/elasticsearch/security/api_key.go +++ b/internal/elasticsearch/security/api_key.go @@ -126,7 +126,7 @@ func resourceSecurityApiKeyCreate(ctx context.Context, d *schema.ResourceData, m apikey.RolesDescriptors = role_descriptors var hasRestriction = false - var keysWithRestrictions = []string{} + var keysWithRestrictions []string for key, roleDescriptor := range role_descriptors { if roleDescriptor.Restriction != nil { @@ -136,31 +136,12 @@ func resourceSecurityApiKeyCreate(ctx context.Context, d *schema.ResourceData, m } if hasRestriction { - if esClient, err := client.GetESClient(); err == nil { - req := esapi.InfoRequest{} - res, err := req.Do(ctx, esClient.Transport) - if err != nil { - return diag.FromErr(err) - } - - defer res.Body.Close() - - var info info - err = json.NewDecoder(res.Body).Decode(&info) - if err != nil { - return diag.Errorf("error decoding Elasticsearch informations: %s", err) - } - - currentVersion, err := version.NewVersion(info.Version.Number) - if err != nil { - return diag.FromErr(err) - } - - supportedVersion, _ := version.NewVersion("8.9.0") - - if currentVersion.LessThan(supportedVersion) && hasRestriction { - return diag.Errorf(`Specifying "restriction" on an API key role description is not supported in this version of Elasticsearch. API keys: %s, role descriptor(s) %s`, apikey.Name, strings.Join(keysWithRestrictions, ", ")) - } + isSupported, err := doesCurrentVersionSupportRestrictionOnApiKey(ctx, client, keysWithRestrictions) + if err != nil { + return diag.FromErr(err) + } + if isSupported { + return diag.Errorf("Specifying `restriction` on an API key role description is not supported in this version of Elasticsearch. Role descriptor(s) %s", strings.Join(keysWithRestrictions, ", ")) } } } @@ -206,6 +187,36 @@ func resourceSecurityApiKeyCreate(ctx context.Context, d *schema.ResourceData, m return resourceSecurityApiKeyRead(ctx, d, meta) } +func doesCurrentVersionSupportRestrictionOnApiKey(ctx context.Context, client *clients.ApiClient, keysWithRestrictions []string) (isSupported bool, err error) { + if esClient, err := client.GetESClient(); err == nil { + req := esapi.InfoRequest{} + res, err := req.Do(ctx, esClient.Transport) + if err != nil { + return false, err + } + + defer res.Body.Close() + + var info info + err = json.NewDecoder(res.Body).Decode(&info) + if err != nil { + return false, err + } + + currentVersion, err := version.NewVersion(info.Version.Number) + if err != nil { + return false, err + } + + supportedVersion, _ := version.NewVersion("8.9.0") // restriction is supported since 8.9.x + + if currentVersion.LessThan(supportedVersion) { + return false, err + } + } + return true, nil +} + func resourceSecurityApiKeyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { diags := diag.Diagnostics{diag.Diagnostic{ Severity: diag.Error, diff --git a/internal/elasticsearch/security/api_key_test.go b/internal/elasticsearch/security/api_key_test.go index 8521f2ebe..0ecf1357c 100644 --- a/internal/elasticsearch/security/api_key_test.go +++ b/internal/elasticsearch/security/api_key_test.go @@ -127,7 +127,7 @@ func TestAccResourceSecurityApiKeyWithWorkflowRestrictionOnElasticPre8_9_x(t *te { SkipFunc: SkipWhenApiKeysAreNotSupportedOrRestrictionsAreSupported(security.APIKeyMinVersion, security.APIKeyWithRestrictionMinVersion), Config: testAccResourceSecurityApiKeyCreateWithWorkflowRestriction(apiKeyName), - ExpectError: regexp.MustCompile(fmt.Sprintf(`Specifying "restriction" on an API key role description is not supported in this version of Elasticsearch. API keys: %s, role descriptor(s) %s`, apiKeyName, "role-a")), + ExpectError: regexp.MustCompile(fmt.Sprintf(`Error: Specifying "restriction" on an API key role description is not supported in this version of Elasticsearch. Role descriptor(s) %s`, "role-a")), }, }, }) @@ -139,12 +139,13 @@ func SkipWhenApiKeysAreNotSupportedOrRestrictionsAreSupported(minApiKeySupported if err != nil { return false, err } - serverVersion, diags := client.ServerVersion(context.Background()) + _, diags := client.ServerVersion(context.Background()) if diags.HasError() { return false, fmt.Errorf("failed to parse the elasticsearch version %v", diags) } - return serverVersion.LessThan(minApiKeySupportedVersion) || serverVersion.GreaterThanOrEqual(minRestrictionSupportedVersion), nil + return false, nil + // return serverVersion.LessThan(minApiKeySupportedVersion) || serverVersion.GreaterThanOrEqual(minRestrictionSupportedVersion), nil } } From 0285b168f55fd47a27afee193b20830175d397e3 Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Thu, 29 Feb 2024 15:01:25 +0100 Subject: [PATCH 08/15] Revert debug change --- internal/elasticsearch/security/api_key_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/elasticsearch/security/api_key_test.go b/internal/elasticsearch/security/api_key_test.go index 0ecf1357c..9a023bfea 100644 --- a/internal/elasticsearch/security/api_key_test.go +++ b/internal/elasticsearch/security/api_key_test.go @@ -139,13 +139,12 @@ func SkipWhenApiKeysAreNotSupportedOrRestrictionsAreSupported(minApiKeySupported if err != nil { return false, err } - _, diags := client.ServerVersion(context.Background()) + serverVersion, diags := client.ServerVersion(context.Background()) if diags.HasError() { return false, fmt.Errorf("failed to parse the elasticsearch version %v", diags) } - return false, nil - // return serverVersion.LessThan(minApiKeySupportedVersion) || serverVersion.GreaterThanOrEqual(minRestrictionSupportedVersion), nil + return serverVersion.LessThan(minApiKeySupportedVersion) || serverVersion.GreaterThanOrEqual(minRestrictionSupportedVersion), nil } } From 3552843918e7835f649564c5a5bc08309ab9861f Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Thu, 29 Feb 2024 15:03:46 +0100 Subject: [PATCH 09/15] Booleans are hard --- internal/elasticsearch/security/api_key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/elasticsearch/security/api_key.go b/internal/elasticsearch/security/api_key.go index b449e8761..9550306ee 100644 --- a/internal/elasticsearch/security/api_key.go +++ b/internal/elasticsearch/security/api_key.go @@ -140,7 +140,7 @@ func resourceSecurityApiKeyCreate(ctx context.Context, d *schema.ResourceData, m if err != nil { return diag.FromErr(err) } - if isSupported { + if !isSupported { return diag.Errorf("Specifying `restriction` on an API key role description is not supported in this version of Elasticsearch. Role descriptor(s) %s", strings.Join(keysWithRestrictions, ", ")) } } From 651754b6bbdeb10856b2ac273bdbddf7d56e17b3 Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Thu, 29 Feb 2024 15:07:55 +0100 Subject: [PATCH 10/15] This is a finicky test --- internal/elasticsearch/security/api_key_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/elasticsearch/security/api_key_test.go b/internal/elasticsearch/security/api_key_test.go index 9a023bfea..14b433927 100644 --- a/internal/elasticsearch/security/api_key_test.go +++ b/internal/elasticsearch/security/api_key_test.go @@ -127,7 +127,7 @@ func TestAccResourceSecurityApiKeyWithWorkflowRestrictionOnElasticPre8_9_x(t *te { SkipFunc: SkipWhenApiKeysAreNotSupportedOrRestrictionsAreSupported(security.APIKeyMinVersion, security.APIKeyWithRestrictionMinVersion), Config: testAccResourceSecurityApiKeyCreateWithWorkflowRestriction(apiKeyName), - ExpectError: regexp.MustCompile(fmt.Sprintf(`Error: Specifying "restriction" on an API key role description is not supported in this version of Elasticsearch. Role descriptor(s) %s`, "role-a")), + ExpectError: regexp.MustCompile(fmt.Sprintf(".*Error: Specifying `restriction` on an API key role description is not supported in this version of Elasticsearch. Role descriptor\\(s\\) %s.*", "role-a")), }, }, }) From 68267839b9e2fe9e74c805d130bd4086af0c9f24 Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Thu, 29 Feb 2024 15:11:36 +0100 Subject: [PATCH 11/15] Format a terraform example file --- .../elasticstack_elasticsearch_security_api_key/resource.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/resources/elasticstack_elasticsearch_security_api_key/resource.tf b/examples/resources/elasticstack_elasticsearch_security_api_key/resource.tf index 97f02bbc2..f974e130b 100644 --- a/examples/resources/elasticstack_elasticsearch_security_api_key/resource.tf +++ b/examples/resources/elasticstack_elasticsearch_security_api_key/resource.tf @@ -41,7 +41,7 @@ resource "elasticstack_elasticsearch_security_api_key" "api_key_with_restriction } ], restriction = { - workflows = [ "search_application_query" ] + workflows = ["search_application_query"] } } }) From 215c798038d3c5d2ddbf3de06c584c2c2fe8a712 Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Tue, 12 Mar 2024 08:54:57 +0100 Subject: [PATCH 12/15] Use the ServerVersion() instead of creating a whole new request --- internal/elasticsearch/security/api_key.go | 41 ++++++---------------- 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/internal/elasticsearch/security/api_key.go b/internal/elasticsearch/security/api_key.go index 9550306ee..680f2048b 100644 --- a/internal/elasticsearch/security/api_key.go +++ b/internal/elasticsearch/security/api_key.go @@ -3,7 +3,6 @@ package security import ( "context" "encoding/json" - "github.com/elastic/go-elasticsearch/v7/esapi" "regexp" "strings" @@ -136,10 +135,12 @@ func resourceSecurityApiKeyCreate(ctx context.Context, d *schema.ResourceData, m } if hasRestriction { - isSupported, err := doesCurrentVersionSupportRestrictionOnApiKey(ctx, client, keysWithRestrictions) - if err != nil { - return diag.FromErr(err) + isSupported, diags := doesCurrentVersionSupportRestrictionOnApiKey(ctx, client) + + if diags.HasError() { + return diags } + if !isSupported { return diag.Errorf("Specifying `restriction` on an API key role description is not supported in this version of Elasticsearch. Role descriptor(s) %s", strings.Join(keysWithRestrictions, ", ")) } @@ -187,34 +188,14 @@ func resourceSecurityApiKeyCreate(ctx context.Context, d *schema.ResourceData, m return resourceSecurityApiKeyRead(ctx, d, meta) } -func doesCurrentVersionSupportRestrictionOnApiKey(ctx context.Context, client *clients.ApiClient, keysWithRestrictions []string) (isSupported bool, err error) { - if esClient, err := client.GetESClient(); err == nil { - req := esapi.InfoRequest{} - res, err := req.Do(ctx, esClient.Transport) - if err != nil { - return false, err - } - - defer res.Body.Close() - - var info info - err = json.NewDecoder(res.Body).Decode(&info) - if err != nil { - return false, err - } +func doesCurrentVersionSupportRestrictionOnApiKey(ctx context.Context, client *clients.ApiClient) (bool, diag.Diagnostics) { + currentVersion, diags := client.ServerVersion(ctx) - currentVersion, err := version.NewVersion(info.Version.Number) - if err != nil { - return false, err - } - - supportedVersion, _ := version.NewVersion("8.9.0") // restriction is supported since 8.9.x - - if currentVersion.LessThan(supportedVersion) { - return false, err - } + if diags.HasError() { + return false, diags } - return true, nil + + return currentVersion.GreaterThanOrEqual(APIKeyWithRestrictionMinVersion), nil } func resourceSecurityApiKeyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { From c80514a446e3446a957dde3fd16db1ea71b2a8d0 Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Tue, 12 Mar 2024 08:58:08 +0100 Subject: [PATCH 13/15] Update docs --- docs/resources/elasticsearch_security_api_key.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/resources/elasticsearch_security_api_key.md b/docs/resources/elasticsearch_security_api_key.md index 47e9e147d..972a3a558 100644 --- a/docs/resources/elasticsearch_security_api_key.md +++ b/docs/resources/elasticsearch_security_api_key.md @@ -56,7 +56,7 @@ resource "elasticstack_elasticsearch_security_api_key" "api_key_with_restriction } ], restriction = { - workflows = [ "search_application_query" ] + workflows = ["search_application_query"] } } }) From e6aef17ec204932d91e8b3827bd2c57400c09cc3 Mon Sep 17 00:00:00 2001 From: Sander van Vliet Date: Tue, 12 Mar 2024 09:05:04 +0100 Subject: [PATCH 14/15] Remove two unused structs --- internal/elasticsearch/security/api_key.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/internal/elasticsearch/security/api_key.go b/internal/elasticsearch/security/api_key.go index 680f2048b..fa8c54414 100644 --- a/internal/elasticsearch/security/api_key.go +++ b/internal/elasticsearch/security/api_key.go @@ -92,16 +92,6 @@ func ResourceApiKey() *schema.Resource { } } -type esVersion struct { - Number string `json:"number"` - BuildFlavor string `json:"build_flavor"` -} - -type info struct { - Version esVersion `json:"version"` - Tagline string `json:"tagline"` -} - func resourceSecurityApiKeyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client, diags := clients.NewApiClientFromSDKResource(d, meta) if diags.HasError() { From a4fd74be2c0b188d9209e7c663214daa752184e3 Mon Sep 17 00:00:00 2001 From: Toby Brain Date: Thu, 8 Aug 2024 14:08:11 +1000 Subject: [PATCH 15/15] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9201c9ce3..ff948f49b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - Fix setting `id` for Fleet outputs and servers ([#666](https://github.com/elastic/terraform-provider-elasticstack/pull/666)) - Fix `elasticstack_fleet_enrollment_tokens` returning empty tokens in some case ([#683](https://github.com/elastic/terraform-provider-elasticstack/pull/683)) - Add support for Kibana synthetics private locations ([#696](https://github.com/elastic/terraform-provider-elasticstack/pull/696)) +- Support setting `restriction` in `elasticstack_elasticsearch_security_api_key` role definitions ([#577](https://github.com/elastic/terraform-provider-elasticstack/pull/577)) ## [0.11.4] - 2024-06-13