Skip to content

Commit

Permalink
add remote_indices to elasticstack_elasticsearch_security_role
Browse files Browse the repository at this point in the history
  • Loading branch information
mholttech committed Aug 22, 2024
1 parent 946e8a0 commit 952183e
Show file tree
Hide file tree
Showing 5 changed files with 426 additions and 15 deletions.
166 changes: 166 additions & 0 deletions internal/elasticsearch/security/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ import (
"github.com/elastic/terraform-provider-elasticstack/internal/clients/elasticsearch"
"github.com/elastic/terraform-provider-elasticstack/internal/models"
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

var minSupportedRemoteIndicesVersion = version.Must(version.NewVersion("8.10.0"))

func ResourceRole() *schema.Resource {
roleSchema := map[string]*schema.Schema{
"id": {
Expand Down Expand Up @@ -137,6 +140,72 @@ func ResourceRole() *schema.Resource {
},
},
},
"remote_indices": {
Description: "A list of remote indices permissions entries. Remote indices are effective for remote clusters configured with the API key based model. They have no effect for remote clusters configured with the certificate based model.",
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"clusters": {
Description: "A list of cluster aliases to which the permissions in this entry apply.",
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"field_security": {
Description: "The document fields that the owners of the role have read access to.",
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"grant": {
Description: "List of the fields to grant the access to.",
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"except": {
Description: "List of the fields to which the grants will not be applied.",
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
"query": {
Description: "A search query that defines the documents the owners of the role have read access to.",
Type: schema.TypeString,
ValidateFunc: validation.StringIsJSON,
DiffSuppressFunc: utils.DiffJsonSuppress,
Optional: true,
},
"names": {
Description: "A list of indices (or index name patterns) to which the permissions in this entry apply.",
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"privileges": {
Description: "The index level privileges that the owners of the role have on the specified indices.",
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
"metadata": {
Description: "Optional meta-data.",
Type: schema.TypeString,
Expand Down Expand Up @@ -178,6 +247,10 @@ func resourceSecurityRolePut(ctx context.Context, d *schema.ResourceData, meta i
if diags.HasError() {
return diags
}
serverVersion, diags := client.ServerVersion(ctx)
if diags.HasError() {
return diags
}
roleId := d.Get("name").(string)
id, diags := client.ID(ctx, roleId)
if diags.HasError() {
Expand Down Expand Up @@ -286,6 +359,71 @@ func resourceSecurityRolePut(ctx context.Context, d *schema.ResourceData, meta i
role.Indices = indices
}

if v, ok := d.GetOk("remote_indices"); ok {
definedRemoteIndices := v.(*schema.Set)
if definedRemoteIndices.Len() > 0 {
if serverVersion.LessThan(minSupportedRemoteIndicesVersion) {
return diag.FromErr(fmt.Errorf("'remote_indices' is supported only for Kibana v%s and above", minSupportedRemoteIndicesVersion.String()))
}
}
remote_indices := make([]models.RemoteIndexPerms, definedRemoteIndices.Len())
for i, idx := range definedRemoteIndices.List() {
remote_index := idx.(map[string]interface{})

definedRemoteNames := remote_index["names"].(*schema.Set)
remote_names := make([]string, definedRemoteNames.Len())
for i, name := range definedRemoteNames.List() {
remote_names[i] = name.(string)
}
definedRemoteClusters := remote_index["clusters"].(*schema.Set)
remote_clusters := make([]string, definedRemoteClusters.Len())
for i, cluster := range definedRemoteClusters.List() {
remote_clusters[i] = cluster.(string)
}
definedRemotePrivs := remote_index["privileges"].(*schema.Set)
remote_privs := make([]string, definedRemotePrivs.Len())
for i, pr := range definedRemotePrivs.List() {
remote_privs[i] = pr.(string)
}

newRemoteIndex := models.RemoteIndexPerms{
Names: remote_names,
Clusters: remote_clusters,
Privileges: remote_privs,
}

if query := remote_index["query"].(string); query != "" {
newRemoteIndex.Query = &query
}
if fieldSec := remote_index["field_security"].([]interface{}); len(fieldSec) > 0 {
remote_fieldSecurity := models.FieldSecurity{}
// there must be only 1 entry
definedRemoteFieldSec := fieldSec[0].(map[string]interface{})

// grants
if gr := definedRemoteFieldSec["grant"].(*schema.Set); gr != nil {
grants := make([]string, gr.Len())
for i, grant := range gr.List() {
grants[i] = grant.(string)
}
remote_fieldSecurity.Grant = grants
}
// except
if exp := definedRemoteFieldSec["except"].(*schema.Set); exp != nil {
excepts := make([]string, exp.Len())
for i, except := range exp.List() {
excepts[i] = except.(string)
}
remote_fieldSecurity.Except = excepts
}
newRemoteIndex.FieldSecurity = &remote_fieldSecurity
}

remote_indices[i] = newRemoteIndex
}
role.RemoteIndices = remote_indices
}

if v, ok := d.GetOk("metadata"); ok {
metadata := make(map[string]interface{})
if err := json.NewDecoder(strings.NewReader(v.(string))).Decode(&metadata); err != nil {
Expand Down Expand Up @@ -362,6 +500,11 @@ func resourceSecurityRoleRead(ctx context.Context, d *schema.ResourceData, meta
if err := d.Set("indices", indices); err != nil {
return diag.FromErr(err)
}
remoteIndexes := role.RemoteIndices
remoteIndices := flattenRemoteIndicesData(&remoteIndexes)
if err := d.Set("remote_indices", remoteIndices); err != nil {
return diag.FromErr(err)
}

if role.Metadata != nil {
metadata, err := json.Marshal(role.Metadata)
Expand Down Expand Up @@ -418,6 +561,29 @@ func flattenIndicesData(indices *[]models.IndexPerms) []interface{} {
}
return make([]interface{}, 0)
}
func flattenRemoteIndicesData(remoteIndices *[]models.RemoteIndexPerms) []interface{} {
if remoteIndices != nil {
oRemoteIndx := make([]interface{}, len(*remoteIndices))

for i, remoteIndex := range *remoteIndices {
oi := make(map[string]interface{})
oi["names"] = remoteIndex.Names
oi["clusters"] = remoteIndex.Clusters
oi["privileges"] = remoteIndex.Privileges
oi["query"] = remoteIndex.Query

if remoteIndex.FieldSecurity != nil {
fsec := make(map[string]interface{})
fsec["grant"] = remoteIndex.FieldSecurity.Grant
fsec["except"] = remoteIndex.FieldSecurity.Except
oi["field_security"] = []interface{}{fsec}
}
oRemoteIndx[i] = oi
}
return oRemoteIndx
}
return make([]interface{}, 0)
}

func resourceSecurityRoleDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client, diags := clients.NewApiClientFromSDKResource(d, meta)
Expand Down
63 changes: 63 additions & 0 deletions internal/elasticsearch/security/role_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,69 @@ func DataSourceRole() *schema.Resource {
},
},
},
"remote_indices": {
Description: "A list of remote indices permissions entries. Remote indices are effective for remote clusters configured with the API key based model. They have no effect for remote clusters configured with the certificate based model.",
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"clusters": {
Description: "A list of cluster aliases to which the permissions in this entry apply.",
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"field_security": {
Description: "The document fields that the owners of the role have read access to.",
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"grant": {
Description: "List of the fields to grant the access to.",
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"except": {
Description: "List of the fields to which the grants will not be applied.",
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
"names": {
Description: "A list of indices (or index name patterns) to which the permissions in this entry apply.",
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"privileges": {
Description: "The index level privileges that the owners of the role have on the specified indices.",
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"query": {
Description: "A search query that defines the documents the owners of the role have read access to.",
Type: schema.TypeString,
Computed: true,
},
},
},
},
"metadata": {
Description: "Optional meta-data.",
Type: schema.TypeString,
Expand Down
64 changes: 64 additions & 0 deletions internal/elasticsearch/security/role_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import (

"github.com/elastic/terraform-provider-elasticstack/internal/acctest"
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
"github.com/elastic/terraform-provider-elasticstack/internal/versionutils"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccDataSourceSecurityRole(t *testing.T) {
minSupportedRemoteIndicesVersion := version.Must(version.NewSemver("8.10.0"))
resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProtoV6ProviderFactories: acctest.Providers,
Expand All @@ -28,6 +31,24 @@ func TestAccDataSourceSecurityRole(t *testing.T) {
resource.TestCheckResourceAttr("data.elasticstack_elasticsearch_security_role.test", "metadata", `{"version":1}`),
),
},
{
Config: testAccDataSourceSecurityRoleRemoteIndices,
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minSupportedRemoteIndicesVersion),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.elasticstack_elasticsearch_security_role.test", "name", "data_source_test"),
resource.TestCheckTypeSetElemAttr("data.elasticstack_elasticsearch_security_role.test", "cluster.*", "all"),
utils.TestCheckResourceListAttr("data.elasticstack_elasticsearch_security_role.test", "indices.0.names", []string{"index1", "index2"}),
resource.TestCheckTypeSetElemAttr("data.elasticstack_elasticsearch_security_role.test", "indices.0.privileges.*", "all"),
resource.TestCheckResourceAttr("data.elasticstack_elasticsearch_security_role.test", "indices.0.allow_restricted_indices", "true"),
resource.TestCheckResourceAttr("data.elasticstack_elasticsearch_security_role.test", "applications.0.application", "myapp"),
utils.TestCheckResourceListAttr("data.elasticstack_elasticsearch_security_role.test", "applications.0.privileges", []string{"admin", "read"}),
resource.TestCheckTypeSetElemAttr("data.elasticstack_elasticsearch_security_role.test", "applications.0.resources.*", "*"),
resource.TestCheckTypeSetElemAttr("data.elasticstack_elasticsearch_security_role.test", "run_as.*", "other_user"),
resource.TestCheckResourceAttr("data.elasticstack_elasticsearch_security_role.test", "metadata", `{"version":1}`),
resource.TestCheckTypeSetElemAttr("data.elasticstack_elasticsearch_security_role.test", "remote_indices.*.clusters.*", "test-cluster2"),
resource.TestCheckTypeSetElemAttr("data.elasticstack_elasticsearch_security_role.test", "remote_indices.*.names.*", "sample2"),
),
},
},
})
}
Expand Down Expand Up @@ -64,3 +85,46 @@ data "elasticstack_elasticsearch_security_role" "test" {
name = elasticstack_elasticsearch_security_role.test.name
}
`

const testAccDataSourceSecurityRoleRemoteIndices = `
provider "elasticstack" {
elasticsearch {}
}
resource "elasticstack_elasticsearch_security_role" "test" {
name = "data_source_test"
cluster = ["all"]
indices {
names = ["index1", "index2"]
privileges = ["all"]
allow_restricted_indices = true
}
remote_indices {
clusters = ["test-cluster2"]
field_security {
grant = ["sample"]
except = []
}
names = ["sample2"]
privileges = ["create", "read", "write"]
}
applications {
application = "myapp"
privileges = ["admin", "read"]
resources = ["*"]
}
run_as = ["other_user"]
metadata = jsonencode({
version = 1
})
}
data "elasticstack_elasticsearch_security_role" "test" {
name = elasticstack_elasticsearch_security_role.test.name
}
`
Loading

0 comments on commit 952183e

Please sign in to comment.