Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom access level to access context manager #2180

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
4 changes: 4 additions & 0 deletions .changelog/3653.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
```release-note:enhancement
access_context_manager: custom access level added to `google_access_context_manager_access_level`

```
187 changes: 187 additions & 0 deletions google-beta/resource_access_context_manager_access_level.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,51 @@ for the AccessLevel to be applied. Default value: "AND" Possible values: ["AND",
},
},
},
ConflictsWith: []string{"custom"},
},
"custom": {
Type: schema.TypeList,
Optional: true,
Description: `Custom access level conditions are set using the Cloud Common Expression Language to represent the necessary conditions for the level to apply to a request.
See CEL spec at: https://github.com/google/cel-spec.`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"expr": {
Type: schema.TypeList,
Required: true,
Description: `Represents a textual expression in the Common Expression Language (CEL) syntax. CEL is a C-like expression language.
This page details the objects and attributes that are used to the build the CEL expressions for
custom access levels - https://cloud.google.com/access-context-manager/docs/custom-access-level-spec.`,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"expression": {
Type: schema.TypeString,
Required: true,
Description: `Textual representation of an expression in Common Expression Language syntax.`,
},
"description": {
Type: schema.TypeString,
Optional: true,
Description: `Description of the expression`,
},
"location": {
Type: schema.TypeString,
Optional: true,
Description: `String indicating the location of the expression for error reporting, e.g. a file name and a position in the file`,
},
"title": {
Type: schema.TypeString,
Optional: true,
Description: `Title for the expression, i.e. a short string describing its purpose.`,
},
},
},
},
},
},
ConflictsWith: []string{"basic"},
},
"description": {
Type: schema.TypeString,
Expand Down Expand Up @@ -255,6 +300,12 @@ func resourceAccessContextManagerAccessLevelCreate(d *schema.ResourceData, meta
} else if v, ok := d.GetOkExists("basic"); !isEmptyValue(reflect.ValueOf(basicProp)) && (ok || !reflect.DeepEqual(v, basicProp)) {
obj["basic"] = basicProp
}
customProp, err := expandAccessContextManagerAccessLevelCustom(d.Get("custom"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("custom"); !isEmptyValue(reflect.ValueOf(customProp)) && (ok || !reflect.DeepEqual(v, customProp)) {
obj["custom"] = customProp
}
parentProp, err := expandAccessContextManagerAccessLevelParent(d.Get("parent"), d, config)
if err != nil {
return err
Expand Down Expand Up @@ -341,6 +392,9 @@ func resourceAccessContextManagerAccessLevelRead(d *schema.ResourceData, meta in
if err := d.Set("basic", flattenAccessContextManagerAccessLevelBasic(res["basic"], d, config)); err != nil {
return fmt.Errorf("Error reading AccessLevel: %s", err)
}
if err := d.Set("custom", flattenAccessContextManagerAccessLevelCustom(res["custom"], d, config)); err != nil {
return fmt.Errorf("Error reading AccessLevel: %s", err)
}
if err := d.Set("name", flattenAccessContextManagerAccessLevelName(res["name"], d, config)); err != nil {
return fmt.Errorf("Error reading AccessLevel: %s", err)
}
Expand Down Expand Up @@ -370,6 +424,12 @@ func resourceAccessContextManagerAccessLevelUpdate(d *schema.ResourceData, meta
} else if v, ok := d.GetOkExists("basic"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, basicProp)) {
obj["basic"] = basicProp
}
customProp, err := expandAccessContextManagerAccessLevelCustom(d.Get("custom"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("custom"); !isEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, customProp)) {
obj["custom"] = customProp
}

obj, err = resourceAccessContextManagerAccessLevelEncoder(d, meta, obj)
if err != nil {
Expand All @@ -395,6 +455,10 @@ func resourceAccessContextManagerAccessLevelUpdate(d *schema.ResourceData, meta
if d.HasChange("basic") {
updateMask = append(updateMask, "basic")
}

if d.HasChange("custom") {
updateMask = append(updateMask, "custom")
}
// updateMask is a URL parameter but not present in the schema, so replaceVars
// won't set it
url, err = addQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
Expand Down Expand Up @@ -605,6 +669,54 @@ func flattenAccessContextManagerAccessLevelBasicConditionsRegions(v interface{},
return v
}

func flattenAccessContextManagerAccessLevelCustom(v interface{}, d *schema.ResourceData, config *Config) interface{} {
if v == nil {
return nil
}
original := v.(map[string]interface{})
if len(original) == 0 {
return nil
}
transformed := make(map[string]interface{})
transformed["expr"] =
flattenAccessContextManagerAccessLevelCustomExpr(original["expr"], d, config)
return []interface{}{transformed}
}
func flattenAccessContextManagerAccessLevelCustomExpr(v interface{}, d *schema.ResourceData, config *Config) interface{} {
if v == nil {
return nil
}
original := v.(map[string]interface{})
if len(original) == 0 {
return nil
}
transformed := make(map[string]interface{})
transformed["expression"] =
flattenAccessContextManagerAccessLevelCustomExprExpression(original["expression"], d, config)
transformed["title"] =
flattenAccessContextManagerAccessLevelCustomExprTitle(original["title"], d, config)
transformed["description"] =
flattenAccessContextManagerAccessLevelCustomExprDescription(original["description"], d, config)
transformed["location"] =
flattenAccessContextManagerAccessLevelCustomExprLocation(original["location"], d, config)
return []interface{}{transformed}
}
func flattenAccessContextManagerAccessLevelCustomExprExpression(v interface{}, d *schema.ResourceData, config *Config) interface{} {
return v
}

func flattenAccessContextManagerAccessLevelCustomExprTitle(v interface{}, d *schema.ResourceData, config *Config) interface{} {
return v
}

func flattenAccessContextManagerAccessLevelCustomExprDescription(v interface{}, d *schema.ResourceData, config *Config) interface{} {
return v
}

func flattenAccessContextManagerAccessLevelCustomExprLocation(v interface{}, d *schema.ResourceData, config *Config) interface{} {
return v
}

func flattenAccessContextManagerAccessLevelName(v interface{}, d *schema.ResourceData, config *Config) interface{} {
return v
}
Expand Down Expand Up @@ -835,6 +947,81 @@ func expandAccessContextManagerAccessLevelBasicConditionsRegions(v interface{},
return v, nil
}

func expandAccessContextManagerAccessLevelCustom(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
l := v.([]interface{})
if len(l) == 0 || l[0] == nil {
return nil, nil
}
raw := l[0]
original := raw.(map[string]interface{})
transformed := make(map[string]interface{})

transformedExpr, err := expandAccessContextManagerAccessLevelCustomExpr(original["expr"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedExpr); val.IsValid() && !isEmptyValue(val) {
transformed["expr"] = transformedExpr
}

return transformed, nil
}

func expandAccessContextManagerAccessLevelCustomExpr(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
l := v.([]interface{})
if len(l) == 0 || l[0] == nil {
return nil, nil
}
raw := l[0]
original := raw.(map[string]interface{})
transformed := make(map[string]interface{})

transformedExpression, err := expandAccessContextManagerAccessLevelCustomExprExpression(original["expression"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedExpression); val.IsValid() && !isEmptyValue(val) {
transformed["expression"] = transformedExpression
}

transformedTitle, err := expandAccessContextManagerAccessLevelCustomExprTitle(original["title"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedTitle); val.IsValid() && !isEmptyValue(val) {
transformed["title"] = transformedTitle
}

transformedDescription, err := expandAccessContextManagerAccessLevelCustomExprDescription(original["description"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedDescription); val.IsValid() && !isEmptyValue(val) {
transformed["description"] = transformedDescription
}

transformedLocation, err := expandAccessContextManagerAccessLevelCustomExprLocation(original["location"], d, config)
if err != nil {
return nil, err
} else if val := reflect.ValueOf(transformedLocation); val.IsValid() && !isEmptyValue(val) {
transformed["location"] = transformedLocation
}

return transformed, nil
}

func expandAccessContextManagerAccessLevelCustomExprExpression(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}

func expandAccessContextManagerAccessLevelCustomExprTitle(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}

func expandAccessContextManagerAccessLevelCustomExprDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}

func expandAccessContextManagerAccessLevelCustomExprLocation(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}

func expandAccessContextManagerAccessLevelParent(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
return v, nil
}
Expand Down
41 changes: 41 additions & 0 deletions google-beta/resource_access_context_manager_access_level_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,26 @@ func testAccCheckAccessContextManagerAccessLevelDestroyProducer(t *testing.T) fu
}
}

func testAccAccessContextManagerAccessLevel_customTest(t *testing.T) {
org := getTestOrgFromEnv(t)

vcrTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAccessContextManagerAccessLevelDestroyProducer(t),
Steps: []resource.TestStep{
{
Config: testAccAccessContextManagerAccessLevel_custom(org, "my policy", "level"),
},
{
ResourceName: "google_access_context_manager_access_level.test-access",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccAccessContextManagerAccessLevel_basic(org, policyTitle, levelTitleName string) string {
return fmt.Sprintf(`
resource "google_access_context_manager_access_policy" "test-access" {
Expand All @@ -105,6 +125,27 @@ resource "google_access_context_manager_access_level" "test-access" {
`, org, policyTitle, levelTitleName, levelTitleName)
}

func testAccAccessContextManagerAccessLevel_custom(org, policyTitle, levelTitleName string) string {
return fmt.Sprintf(`
resource "google_access_context_manager_access_policy" "test-access" {
parent = "organizations/%s"
title = "%s"
}

resource "google_access_context_manager_access_level" "test-access" {
parent = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}"
name = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}/accessLevels/%s"
title = "%s"
description = "hello"
custom {
expr {
expression = "device.os_type == OsType.DESKTOP_MAC"
}
}
}
`, org, policyTitle, levelTitleName, levelTitleName)
}

func testAccAccessContextManagerAccessLevel_basicUpdated(org, policyTitle, levelTitleName string) string {
return fmt.Sprintf(`
resource "google_access_context_manager_access_policy" "test-access" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func TestAccAccessContextManager(t *testing.T) {
"service_perimeter_resource": testAccAccessContextManagerServicePerimeterResource_basicTest,
"access_level": testAccAccessContextManagerAccessLevel_basicTest,
"access_level_full": testAccAccessContextManagerAccessLevel_fullTest,
"access_level_custom": testAccAccessContextManagerAccessLevel_customTest,
}

for name, tc := range testCases {
Expand Down
32 changes: 32 additions & 0 deletions website/docs/r/access_context_manager_access_level.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ The following arguments are supported:
(Optional)
A set of predefined conditions for the access level and a combining function. Structure is documented below.

* `custom` -
(Optional)
Custom access level conditions are set using the Cloud Common Expression Language to represent the necessary conditions for the level to apply to a request.
See CEL spec at: https://github.com/google/cel-spec. Structure is documented below.


The `basic` block supports:

Expand Down Expand Up @@ -219,6 +224,33 @@ The `os_constraints` block supports:
* `DESKTOP_LINUX`
* `DESKTOP_CHROME_OS`

The `custom` block supports:

* `expr` -
(Required)
Represents a textual expression in the Common Expression Language (CEL) syntax. CEL is a C-like expression language.
This page details the objects and attributes that are used to the build the CEL expressions for
custom access levels - https://cloud.google.com/access-context-manager/docs/custom-access-level-spec. Structure is documented below.


The `expr` block supports:

* `expression` -
(Required)
Textual representation of an expression in Common Expression Language syntax.

* `title` -
(Optional)
Title for the expression, i.e. a short string describing its purpose.

* `description` -
(Optional)
Description of the expression

* `location` -
(Optional)
String indicating the location of the expression for error reporting, e.g. a file name and a position in the file

## Attributes Reference

In addition to the arguments listed above, the following computed attributes are exported:
Expand Down