diff --git a/internal/provider/policy_data_source.go b/internal/provider/policy_data_source.go index 8f6e3fb..ea5048f 100644 --- a/internal/provider/policy_data_source.go +++ b/internal/provider/policy_data_source.go @@ -74,12 +74,10 @@ You may also optionally provide one or more 'when' and 'unless' conditions as bl MarkdownDescription: "Specifies the principal component of the policy scope. Equivalent to writing 'principal in'", Optional: true, }, - "principal_in": schema.ListAttribute{ + "principal_in": schema.ObjectAttribute{ MarkdownDescription: "Specifies the principal component of the policy scope. Equivalent to writing 'principal =='", Optional: true, - ElementType: basetypes.ObjectType{ - AttrTypes: eid.EIDAttrsForDataSource, - }, + AttributeTypes: eid.EIDAttrsForDataSource, }, "any_action": schema.BoolAttribute{ @@ -112,12 +110,10 @@ You may also optionally provide one or more 'when' and 'unless' conditions as bl MarkdownDescription: "Specifies the resource component of the policy scope. Equivalent to writing 'resource is'", Optional: true, }, - "resource_in": schema.ListAttribute{ + "resource_in": schema.ObjectAttribute{ MarkdownDescription: "Specifies the resource component of the policy scope. Equivalent to writing 'resource in'", Optional: true, - ElementType: basetypes.ObjectType{ - AttrTypes: eid.EIDAttrsForDataSource, - }, + AttributeTypes: eid.EIDAttrsForDataSource, }, }, Blocks: map[string]schema.Block{ diff --git a/internal/provider/policy_data_source_test.go b/internal/provider/policy_data_source_test.go index 8ab6f10..e5e223f 100644 --- a/internal/provider/policy_data_source_test.go +++ b/internal/provider/policy_data_source_test.go @@ -71,3 +71,44 @@ func TestPolicyDataSource_Annotations(t *testing.T) { }, }) } + +func TestPolicyDataSource_InOperator(t *testing.T) { + resource.UnitTest(t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_5_0), + }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: ` + data "cedar_policyset" "test" { + policy { + effect = "permit" + principal_in = { + type = "Group" + id = "test" + } + action_in = [ + { + type = "Action" + id = "Read" + } + ] + resource_in = { + type = "Folder" + id = "example" + } + } + } + + output "test" { + value = data.cedar_policyset.test.text + } + `, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckOutput("test", "permit (\n\tprincipal in Group::\"test\",\n\taction in [Action::\"Read\"],\n\tresource in Folder::\"example\"\n);\n"), + ), + }, + }, + }) +} diff --git a/pkg/cedarpolicy/policy.go b/pkg/cedarpolicy/policy.go index 9d303f5..94bafef 100644 --- a/pkg/cedarpolicy/policy.go +++ b/pkg/cedarpolicy/policy.go @@ -20,7 +20,7 @@ type Policy struct { AnyPrincipal types.Bool `tfsdk:"any_principal"` Principal *eid.EID `tfsdk:"principal"` - PrincipalIn *[]eid.EID `tfsdk:"principal_in"` + PrincipalIn *eid.EID `tfsdk:"principal_in"` PrincipalIs types.String `tfsdk:"principal_is"` AnyAction types.Bool `tfsdk:"any_action"` @@ -29,7 +29,7 @@ type Policy struct { AnyResource types.Bool `tfsdk:"any_resource"` Resource *eid.EID `tfsdk:"resource"` - ResourceIn *[]eid.EID `tfsdk:"resource_in"` + ResourceIn *eid.EID `tfsdk:"resource_in"` ResourceIs types.String `tfsdk:"resource_is"` When []Condition `tfsdk:"when"` diff --git a/pkg/cedarpolicy/render_string.go b/pkg/cedarpolicy/render_string.go index dddd6e5..2c11ceb 100644 --- a/pkg/cedarpolicy/render_string.go +++ b/pkg/cedarpolicy/render_string.go @@ -53,24 +53,19 @@ func (p Policy) RenderString() (string, error) { line := fmt.Sprintf("\tprincipal == %s::%q,", principalType, principalID) output = append(output, line) } else if p.PrincipalIn != nil { - // principal == [, ], + // principal in , - entities := make([]string, len(*p.PrincipalIn)) + typ := p.PrincipalIn.Type.ValueString() + id := p.PrincipalIn.ID.ValueString() - for i, ent := range *p.PrincipalIn { - typ := ent.Type.ValueString() - id := ent.ID.ValueString() - - if typ == "" { - return "", fmt.Errorf("principal_in entry %v: type must be specified", i) - } - if id == "" { - return "", fmt.Errorf("principal_in entry %v: ID must be specified", i) - } - entities[i] = fmt.Sprintf(`%s::"%s"`, typ, id) + if typ == "" { + return "", fmt.Errorf("principal_in: type must be specified") + } + if id == "" { + return "", fmt.Errorf("principal_in: ID must be specified") } - line := fmt.Sprintf("\tprincipal in [%s],", strings.Join(entities, ", ")) + line := fmt.Sprintf("\tprincipal in %s::%q,", typ, id) output = append(output, line) } else if p.PrincipalIs.ValueString() != "" { // principal is , @@ -139,24 +134,19 @@ func (p Policy) RenderString() (string, error) { line := fmt.Sprintf("\tresource == %s::%q", resourceType, resourceID) output = append(output, line) } else if p.ResourceIn != nil { - // resource == [, ], + // resource in , , - entities := make([]string, len(*p.ResourceIn)) + typ := p.ResourceIn.Type.ValueString() + id := p.ResourceIn.ID.ValueString() - for i, ent := range *p.ResourceIn { - typ := ent.Type.ValueString() - id := ent.ID.ValueString() - - if typ == "" { - return "", fmt.Errorf("resource_in entry %v: type must be specified", i) - } - if id == "" { - return "", fmt.Errorf("resource_in entry %v: ID must be specified", i) - } - entities[i] = fmt.Sprintf(`%s::"%s"`, typ, id) + if typ == "" { + return "", fmt.Errorf("resource_in: type must be specified") + } + if id == "" { + return "", fmt.Errorf("resource_in: ID must be specified") } - line := fmt.Sprintf("\tresource in [%s]", strings.Join(entities, ", ")) + line := fmt.Sprintf("\tresource in %s::%q", typ, id) output = append(output, line) } else if p.ResourceIs.ValueString() != "" { // resource is , diff --git a/pkg/cedarpolicy/render_string_test.go b/pkg/cedarpolicy/render_string_test.go index b70bca0..cc103e3 100644 --- a/pkg/cedarpolicy/render_string_test.go +++ b/pkg/cedarpolicy/render_string_test.go @@ -139,11 +139,9 @@ unless { name: "principal_action_resource_in", policy: Policy{ Effect: types.StringValue("permit"), - PrincipalIn: &[]eid.EID{ - { - Type: types.StringValue("CF::User"), - ID: types.StringValue("user1"), - }, + PrincipalIn: &eid.EID{ + Type: types.StringValue("CF::User"), + ID: types.StringValue("user1"), }, ActionIn: &[]eid.EID{ @@ -153,32 +151,24 @@ unless { }, }, - ResourceIn: &[]eid.EID{ - { - Type: types.StringValue("Test::Vault"), - ID: types.StringValue("test1"), - }, + ResourceIn: &eid.EID{ + Type: types.StringValue("Test::Vault"), + ID: types.StringValue("test1"), }, }, want: `permit ( - principal in [CF::User::"user1"], + principal in CF::User::"user1", action in [Action::Access::"Request"], - resource in [Test::Vault::"test1"] + resource in Test::Vault::"test1" );`, }, { name: "in_condition_multiple_values", policy: Policy{ Effect: types.StringValue("permit"), - PrincipalIn: &[]eid.EID{ - { - Type: types.StringValue("CF::User"), - ID: types.StringValue("user1"), - }, - { - Type: types.StringValue("CF::User"), - ID: types.StringValue("user2"), - }, + PrincipalIn: &eid.EID{ + Type: types.StringValue("CF::User"), + ID: types.StringValue("user1"), }, ActionIn: &[]eid.EID{ @@ -192,21 +182,15 @@ unless { }, }, - ResourceIn: &[]eid.EID{ - { - Type: types.StringValue("Test::Vault"), - ID: types.StringValue("test1"), - }, - { - Type: types.StringValue("Test::Vault"), - ID: types.StringValue("test2"), - }, + ResourceIn: &eid.EID{ + Type: types.StringValue("Test::Vault"), + ID: types.StringValue("test1"), }, }, want: `permit ( - principal in [CF::User::"user1", CF::User::"user2"], + principal in CF::User::"user1", action in [Action::Access::"Request", Action::Access::"Close"], - resource in [Test::Vault::"test1", Test::Vault::"test2"] + resource in Test::Vault::"test1" );`, }, {