internal/clients/elasticsearch/security.go (370 lines of code) (raw):
package elasticsearch
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"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"
)
func PutUser(ctx context.Context, apiClient *clients.ApiClient, user *models.User) diag.Diagnostics {
var diags diag.Diagnostics
userBytes, err := json.Marshal(user)
if err != nil {
return diag.FromErr(err)
}
esClient, err := apiClient.GetESClient()
if err != nil {
return diag.FromErr(err)
}
res, err := esClient.Security.PutUser(user.Username, bytes.NewReader(userBytes), esClient.Security.PutUser.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to create or update a user"); diags.HasError() {
return diags
}
return diags
}
func GetUser(ctx context.Context, apiClient *clients.ApiClient, username string) (*models.User, diag.Diagnostics) {
var diags diag.Diagnostics
esClient, err := apiClient.GetESClient()
if err != nil {
return nil, diag.FromErr(err)
}
req := esClient.Security.GetUser.WithUsername(username)
res, err := esClient.Security.GetUser(req, esClient.Security.GetUser.WithContext(ctx))
if err != nil {
return nil, diag.FromErr(err)
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
return nil, nil
}
if diags := utils.CheckError(res, "Unable to get a user."); diags.HasError() {
return nil, diags
}
// unmarshal our response to proper type
users := make(map[string]models.User)
if err := json.NewDecoder(res.Body).Decode(&users); err != nil {
return nil, diag.FromErr(err)
}
if user, ok := users[username]; ok {
return &user, diags
}
diags = append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "Unable to find a user in the cluster",
Detail: fmt.Sprintf(`Unable to find "%s" user in the cluster`, username),
})
return nil, diags
}
func DeleteUser(ctx context.Context, apiClient *clients.ApiClient, username string) diag.Diagnostics {
var diags diag.Diagnostics
esClient, err := apiClient.GetESClient()
if err != nil {
return diag.FromErr(err)
}
res, err := esClient.Security.DeleteUser(username, esClient.Security.DeleteUser.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to delete a user"); diags.HasError() {
return diags
}
return diags
}
func EnableUser(ctx context.Context, apiClient *clients.ApiClient, username string) diag.Diagnostics {
var diags diag.Diagnostics
esClient, err := apiClient.GetESClient()
if err != nil {
return diag.FromErr(err)
}
res, err := esClient.Security.EnableUser(username, esClient.Security.EnableUser.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to enable system user"); diags.HasError() {
return diags
}
return diags
}
func DisableUser(ctx context.Context, apiClient *clients.ApiClient, username string) diag.Diagnostics {
var diags diag.Diagnostics
esClient, err := apiClient.GetESClient()
if err != nil {
return diag.FromErr(err)
}
res, err := esClient.Security.DisableUser(username, esClient.Security.DisableUser.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to disable system user"); diags.HasError() {
return diags
}
return diags
}
func ChangeUserPassword(ctx context.Context, apiClient *clients.ApiClient, username string, userPassword *models.UserPassword) diag.Diagnostics {
var diags diag.Diagnostics
userPasswordBytes, err := json.Marshal(userPassword)
if err != nil {
return diag.FromErr(err)
}
esClient, err := apiClient.GetESClient()
if err != nil {
return diag.FromErr(err)
}
res, err := esClient.Security.ChangePassword(
bytes.NewReader(userPasswordBytes),
esClient.Security.ChangePassword.WithUsername(username),
esClient.Security.ChangePassword.WithContext(ctx),
)
if err != nil {
return diag.FromErr(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to change user's password"); diags.HasError() {
return diags
}
return diags
}
func PutRole(ctx context.Context, apiClient *clients.ApiClient, role *models.Role) diag.Diagnostics {
var diags diag.Diagnostics
roleBytes, err := json.Marshal(role)
if err != nil {
return diag.FromErr(err)
}
esClient, err := apiClient.GetESClient()
if err != nil {
return diag.FromErr(err)
}
res, err := esClient.Security.PutRole(role.Name, bytes.NewReader(roleBytes), esClient.Security.PutRole.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to create role"); diags.HasError() {
return diags
}
return diags
}
func GetRole(ctx context.Context, apiClient *clients.ApiClient, rolename string) (*models.Role, diag.Diagnostics) {
var diags diag.Diagnostics
esClient, err := apiClient.GetESClient()
if err != nil {
return nil, diag.FromErr(err)
}
req := esClient.Security.GetRole.WithName(rolename)
res, err := esClient.Security.GetRole(req, esClient.Security.GetRole.WithContext(ctx))
if err != nil {
return nil, diag.FromErr(err)
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
return nil, nil
}
if diags := utils.CheckError(res, "Unable to get a role."); diags.HasError() {
return nil, diags
}
roles := make(map[string]models.Role)
if err := json.NewDecoder(res.Body).Decode(&roles); err != nil {
return nil, diag.FromErr(err)
}
if role, ok := roles[rolename]; ok {
return &role, diags
}
diags = append(diags, diag.Diagnostic{
Severity: diag.Error,
Summary: "Unable to find a role in the cluster",
Detail: fmt.Sprintf(`Unable to find "%s" role in the cluster`, rolename),
})
return nil, diags
}
func DeleteRole(ctx context.Context, apiClient *clients.ApiClient, rolename string) diag.Diagnostics {
var diags diag.Diagnostics
esClient, err := apiClient.GetESClient()
if err != nil {
return diag.FromErr(err)
}
res, err := esClient.Security.DeleteRole(rolename, esClient.Security.DeleteRole.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to delete role"); diags.HasError() {
return diags
}
return diags
}
func PutRoleMapping(ctx context.Context, apiClient *clients.ApiClient, roleMapping *models.RoleMapping) diag.Diagnostics {
roleMappingBytes, err := json.Marshal(roleMapping)
if err != nil {
return diag.FromErr(err)
}
esClient, err := apiClient.GetESClient()
if err != nil {
return diag.FromErr(err)
}
res, err := esClient.Security.PutRoleMapping(roleMapping.Name, bytes.NewReader(roleMappingBytes), esClient.Security.PutRoleMapping.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to put role mapping"); diags.HasError() {
return diags
}
return nil
}
func GetRoleMapping(ctx context.Context, apiClient *clients.ApiClient, roleMappingName string) (*models.RoleMapping, diag.Diagnostics) {
esClient, err := apiClient.GetESClient()
if err != nil {
return nil, diag.FromErr(err)
}
req := esClient.Security.GetRoleMapping.WithName(roleMappingName)
res, err := esClient.Security.GetRoleMapping(req, esClient.Security.GetRoleMapping.WithContext(ctx))
if err != nil {
return nil, diag.FromErr(err)
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
return nil, nil
}
if diags := utils.CheckError(res, "Unable to get a role mapping."); diags.HasError() {
return nil, diags
}
roleMappings := make(map[string]models.RoleMapping)
if err := json.NewDecoder(res.Body).Decode(&roleMappings); err != nil {
return nil, diag.FromErr(err)
}
if roleMapping, ok := roleMappings[roleMappingName]; ok {
roleMapping.Name = roleMappingName
return &roleMapping, nil
}
return nil, diag.Errorf("unable to find role mapping '%s' in the cluster", roleMappingName)
}
func DeleteRoleMapping(ctx context.Context, apiClient *clients.ApiClient, roleMappingName string) diag.Diagnostics {
esClient, err := apiClient.GetESClient()
if err != nil {
return diag.FromErr(err)
}
res, err := esClient.Security.DeleteRoleMapping(roleMappingName, esClient.Security.DeleteRoleMapping.WithContext(ctx))
if err != nil {
return diag.FromErr(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to delete role mapping"); diags.HasError() {
return diags
}
return nil
}
func CreateApiKey(apiClient *clients.ApiClient, apikey *models.ApiKey) (*models.ApiKeyCreateResponse, fwdiag.Diagnostics) {
apikeyBytes, err := json.Marshal(apikey)
if err != nil {
return nil, utils.FrameworkDiagFromError(err)
}
esClient, err := apiClient.GetESClient()
if err != nil {
return nil, utils.FrameworkDiagFromError(err)
}
res, err := esClient.Security.CreateAPIKey(bytes.NewReader(apikeyBytes))
if err != nil {
return nil, utils.FrameworkDiagFromError(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to create apikey"); diags.HasError() {
return nil, utils.FrameworkDiagsFromSDK(diags)
}
var apiKey models.ApiKeyCreateResponse
if err := json.NewDecoder(res.Body).Decode(&apiKey); err != nil {
return nil, utils.FrameworkDiagFromError(err)
}
return &apiKey, nil
}
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 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 update 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, utils.FrameworkDiagFromError(err)
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
return nil, nil
}
if diags := utils.CheckError(res, "Unable to get an apikey."); diags.HasError() {
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, utils.FrameworkDiagFromError(err)
}
if len(apiKeys.ApiKeys) != 1 {
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, nil
}
func DeleteApiKey(apiClient *clients.ApiClient, id string) fwdiag.Diagnostics {
apiKeys := struct {
Ids []string `json:"ids"`
}{
[]string{id},
}
apikeyBytes, err := json.Marshal(apiKeys)
if err != nil {
return utils.FrameworkDiagFromError(err)
}
esClient, err := apiClient.GetESClient()
if err != nil {
return utils.FrameworkDiagFromError(err)
}
res, err := esClient.Security.InvalidateAPIKey(bytes.NewReader(apikeyBytes))
if err != nil && res.IsError() {
return utils.FrameworkDiagFromError(err)
}
defer res.Body.Close()
if diags := utils.CheckError(res, "Unable to delete an apikey"); diags.HasError() {
return utils.FrameworkDiagsFromSDK(diags)
}
return nil
}