Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## [Unreleased]

- Support updating `elasticstack_elasticsearch_security_api_key` when supported by the backing cluster ([#843](https://github.com/elastic/terraform-provider-elasticstack/pull/843))
- Fix validation of `throttle`, and `interval` attributes in `elasticstack_kibana_alerting_rule` allowing all Elastic duration values ([#846](https://github.com/elastic/terraform-provider-elasticstack/pull/846))
- Fix boolean setting parsing for `elasticstack_elasticsearch_indices` data source. ([#842](https://github.com/elastic/terraform-provider-elasticstack/pull/842))

Expand Down
2 changes: 1 addition & 1 deletion docs/resources/elasticsearch_security_api_key.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ output "api_key" {

### Optional

- `elasticsearch_connection` (Block List, Max: 1, Deprecated) Elasticsearch connection configuration block. This property will be removed in a future provider version. Configure the Elasticsearch connection via the provider configuration instead. (see [below for nested schema](#nestedblock--elasticsearch_connection))
- `elasticsearch_connection` (Block List, Deprecated) Elasticsearch connection configuration block. (see [below for nested schema](#nestedblock--elasticsearch_connection))
- `expiration` (String) Expiration time for the API key. By default, API keys never expire.
- `metadata` (String) Arbitrary metadata that you want to associate with the API key.
- `role_descriptors` (String) Role descriptors for this API key.
Expand Down
91 changes: 55 additions & 36 deletions internal/clients/elasticsearch/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/elastic/terraform-provider-elasticstack/internal/clients"
"github.com/elastic/terraform-provider-elasticstack/internal/models"
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
fwdiag "github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
)

Expand Down Expand Up @@ -292,83 +293,101 @@ func DeleteRoleMapping(ctx context.Context, apiClient *clients.ApiClient, roleMa
return nil
}

func PutApiKey(apiClient *clients.ApiClient, apikey *models.ApiKey) (*models.ApiKeyResponse, diag.Diagnostics) {
var diags diag.Diagnostics
func CreateApiKey(apiClient *clients.ApiClient, apikey *models.ApiKey) (*models.ApiKeyCreateResponse, fwdiag.Diagnostics) {
apikeyBytes, err := json.Marshal(apikey)
if err != nil {
return nil, diag.FromErr(err)
return nil, utils.FrameworkDiagFromError(err)
}

esClient, err := apiClient.GetESClient()
if err != nil {
return nil, diag.FromErr(err)
return nil, utils.FrameworkDiagFromError(err)
}
res, err := esClient.Security.CreateAPIKey(bytes.NewReader(apikeyBytes))
if err != nil {
return nil, diag.FromErr(err)
return nil, utils.FrameworkDiagFromError(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to create apikey"); diags.HasError() {
return nil, diags
return nil, utils.FrameworkDiagsFromSDK(diags)
}

var apiKey models.ApiKeyResponse
var apiKey models.ApiKeyCreateResponse

if err := json.NewDecoder(res.Body).Decode(&apiKey); err != nil {
return nil, diag.FromErr(err)
return nil, utils.FrameworkDiagFromError(err)
}

return &apiKey, diags
return &apiKey, nil
}

func GetApiKey(apiClient *clients.ApiClient, id string) (*models.ApiKeyResponse, diag.Diagnostics) {
var diags diag.Diagnostics
func UpdateApiKey(apiClient *clients.ApiClient, apikey models.ApiKey) fwdiag.Diagnostics {
id := apikey.ID

apikey.Expiration = ""
apikey.Name = ""
apikey.ID = ""
apikeyBytes, err := json.Marshal(apikey)
if err != nil {
return utils.FrameworkDiagFromError(err)
}

esClient, err := apiClient.GetESClient()
if err != nil {
return nil, diag.FromErr(err)
return utils.FrameworkDiagFromError(err)
}
res, err := esClient.Security.UpdateAPIKey(id, esClient.Security.UpdateAPIKey.WithBody(bytes.NewReader(apikeyBytes)))
if err != nil {
return utils.FrameworkDiagFromError(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to create apikey"); diags.HasError() {
return utils.FrameworkDiagsFromSDK(diags)
}

return nil
}

func GetApiKey(apiClient *clients.ApiClient, id string) (*models.ApiKeyResponse, fwdiag.Diagnostics) {
esClient, err := apiClient.GetESClient()
if err != nil {
return nil, utils.FrameworkDiagFromError(err)
}
req := esClient.Security.GetAPIKey.WithID(id)
res, err := esClient.Security.GetAPIKey(req)
if err != nil {
return nil, diag.FromErr(err)
return nil, utils.FrameworkDiagFromError(err)
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
diags := append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "Unable to find an apikey in the cluster.",
Detail: fmt.Sprintf("Unable to get apikey: '%s' from the cluster.", id),
})
return nil, diags
return nil, nil
}
if diags := utils.CheckError(res, "Unable to get an apikey."); diags.HasError() {
return nil, diags
return nil, utils.FrameworkDiagsFromSDK(diags)
}

// unmarshal our response to proper type
var apiKeys struct {
ApiKeys []models.ApiKeyResponse `json:"api_keys"`
}
if err := json.NewDecoder(res.Body).Decode(&apiKeys); err != nil {
return nil, diag.FromErr(err)
return nil, utils.FrameworkDiagFromError(err)
}

if len(apiKeys.ApiKeys) != 1 {
diags = append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "Unable to find an apikey in the cluster",
Detail: fmt.Sprintf(`Unable to find "%s" apikey in the cluster`, id),
})
return nil, diags
return nil, fwdiag.Diagnostics{
fwdiag.NewErrorDiagnostic(
"Unable to find an apikey in the cluster",
fmt.Sprintf(`Unable to find "%s" apikey in the cluster`, id),
),
}
}

apiKey := apiKeys.ApiKeys[0]
return &apiKey, diags
return &apiKey, nil
}

func DeleteApiKey(apiClient *clients.ApiClient, id string) diag.Diagnostics {
var diags diag.Diagnostics

func DeleteApiKey(apiClient *clients.ApiClient, id string) fwdiag.Diagnostics {
apiKeys := struct {
Ids []string `json:"ids"`
}{
Expand All @@ -377,19 +396,19 @@ func DeleteApiKey(apiClient *clients.ApiClient, id string) diag.Diagnostics {

apikeyBytes, err := json.Marshal(apiKeys)
if err != nil {
return diag.FromErr(err)
return utils.FrameworkDiagFromError(err)
}
esClient, err := apiClient.GetESClient()
if err != nil {
return diag.FromErr(err)
return utils.FrameworkDiagFromError(err)
}
res, err := esClient.Security.InvalidateAPIKey(bytes.NewReader(apikeyBytes))
if err != nil && res.IsError() {
return diag.FromErr(err)
return utils.FrameworkDiagFromError(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to delete an apikey"); diags.HasError() {
return diags
return utils.FrameworkDiagsFromSDK(diags)
}
return diags
return nil
}
Loading
Loading