diff --git a/mmv1/products/accesscontextmanager/AccessLevel.yaml b/mmv1/products/accesscontextmanager/AccessLevel.yaml index 471c23829f69..e2639e7e0a51 100644 --- a/mmv1/products/accesscontextmanager/AccessLevel.yaml +++ b/mmv1/products/accesscontextmanager/AccessLevel.yaml @@ -243,6 +243,22 @@ properties: countries/regions. Format: A valid ISO 3166-1 alpha-2 code. item_type: Api::Type::String + - !ruby/object:Api::Type::Array + name: 'vpcNetworkSources' + description: 'The request must originate from one of the provided VPC networks in Google Cloud. Cannot specify this field together with `ip_subnetworks`.' + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::NestedObject + name: 'vpcSubnetwork' + description: 'Sub networks within a VPC network.' + properties: + - !ruby/object:Api::Type::String + name: 'network' + description: 'Required. Network name to be allowed by this Access Level. Networks of foreign organizations requires `compute.network.get` permission to be granted to caller.' + - !ruby/object:Api::Type::Array + name: 'vpcIpSubnetworks' + description: 'CIDR block IP subnetwork specification. Must be IPv4.' + item_type: Api::Type::String - !ruby/object:Api::Type::NestedObject name: 'custom' description: | diff --git a/mmv1/products/accesscontextmanager/AccessLevelCondition.yaml b/mmv1/products/accesscontextmanager/AccessLevelCondition.yaml index e8fcca417b03..403c2f4db7af 100644 --- a/mmv1/products/accesscontextmanager/AccessLevelCondition.yaml +++ b/mmv1/products/accesscontextmanager/AccessLevelCondition.yaml @@ -212,3 +212,19 @@ properties: countries/regions. Format: A valid ISO 3166-1 alpha-2 code. item_type: Api::Type::String + - !ruby/object:Api::Type::Array + name: 'vpcNetworkSources' + description: 'The request must originate from one of the provided VPC networks in Google Cloud. Cannot specify this field together with `ip_subnetworks`.' + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::NestedObject + name: 'vpcSubnetwork' + description: 'Sub networks within a VPC network.' + properties: + - !ruby/object:Api::Type::String + name: 'network' + description: 'Required. Network name to be allowed by this Access Level. Networks of foreign organizations requires `compute.network.get` permission to be granted to caller.' + - !ruby/object:Api::Type::Array + name: 'vpcIpSubnetworks' + description: 'CIDR block IP subnetwork specification. Must be IPv4.' + item_type: Api::Type::String diff --git a/mmv1/products/accesscontextmanager/AccessLevels.yaml b/mmv1/products/accesscontextmanager/AccessLevels.yaml index 2a45c3cc0563..4d2a5933702d 100644 --- a/mmv1/products/accesscontextmanager/AccessLevels.yaml +++ b/mmv1/products/accesscontextmanager/AccessLevels.yaml @@ -234,6 +234,22 @@ properties: countries/regions. Format: A valid ISO 3166-1 alpha-2 code. item_type: Api::Type::String + - !ruby/object:Api::Type::Array + name: 'vpcNetworkSources' + description: 'The request must originate from one of the provided VPC networks in Google Cloud. Cannot specify this field together with `ip_subnetworks`.' + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::NestedObject + name: 'vpcSubnetwork' + description: 'Sub networks within a VPC network.' + properties: + - !ruby/object:Api::Type::String + name: 'network' + description: 'Required. Network name to be allowed by this Access Level. Networks of foreign organizations requires `compute.network.get` permission to be granted to caller.' + - !ruby/object:Api::Type::Array + name: 'vpcIpSubnetworks' + description: 'CIDR block IP subnetwork specification. Must be IPv4.' + item_type: Api::Type::String - !ruby/object:Api::Type::NestedObject name: 'custom' description: | diff --git a/mmv1/products/accesscontextmanager/ServicePerimeter.yaml b/mmv1/products/accesscontextmanager/ServicePerimeter.yaml index dfeb24421dbf..b79c83c666cf 100644 --- a/mmv1/products/accesscontextmanager/ServicePerimeter.yaml +++ b/mmv1/products/accesscontextmanager/ServicePerimeter.yaml @@ -346,6 +346,21 @@ properties: - :ANY_IDENTITY - :ANY_USER_ACCOUNT - :ANY_SERVICE_ACCOUNT + - !ruby/object:Api::Type::Array + name: 'sources' + description: 'Sources that this EgressPolicy authorizes access from.' + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'accessLevel' + description: 'An AccessLevel resource name that allows resources outside the ServicePerimeter to be accessed from the inside.' + - !ruby/object:Api::Type::Enum + name: 'sourceRestriction' + description: 'Whether to enforce traffic restrictions based on `sources` field. If the `sources` field is non-empty, then this field must be set to `SOURCE_RESTRICTION_ENABLED`.' + values: + - :SOURCE_RESTRICTION_UNSPECIFIED + - :SOURCE_RESTRICTION_ENABLED + - :SOURCE_RESTRICTION_DISABLED - !ruby/object:Api::Type::Array name: 'identities' description: | @@ -619,6 +634,21 @@ properties: - :ANY_IDENTITY - :ANY_USER_ACCOUNT - :ANY_SERVICE_ACCOUNT + - !ruby/object:Api::Type::Array + name: 'sources' + description: 'Sources that this EgressPolicy authorizes access from.' + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'accessLevel' + description: 'An AccessLevel resource name that allows resources outside the ServicePerimeter to be accessed from the inside.' + - !ruby/object:Api::Type::Enum + name: 'sourceRestriction' + description: 'Whether to enforce traffic restrictions based on `sources` field. If the `sources` field is non-empty, then this field must be set to `SOURCE_RESTRICTION_ENABLED`.' + values: + - :SOURCE_RESTRICTION_UNSPECIFIED + - :SOURCE_RESTRICTION_ENABLED + - :SOURCE_RESTRICTION_DISABLED - !ruby/object:Api::Type::Array name: 'identities' description: | diff --git a/mmv1/products/accesscontextmanager/ServicePerimeterEgressPolicy.yaml b/mmv1/products/accesscontextmanager/ServicePerimeterEgressPolicy.yaml index 8ff821cb691b..5e46e6770c0d 100644 --- a/mmv1/products/accesscontextmanager/ServicePerimeterEgressPolicy.yaml +++ b/mmv1/products/accesscontextmanager/ServicePerimeterEgressPolicy.yaml @@ -79,6 +79,21 @@ properties: Should be in the format of email address. The email address should represent individual user or service account only. item_type: Api::Type::String + - !ruby/object:Api::Type::Array + name: 'sources' + description: 'Sources that this EgressPolicy authorizes access from.' + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'accessLevel' + description: 'An AccessLevel resource name that allows resources outside the ServicePerimeter to be accessed from the inside.' + - !ruby/object:Api::Type::Enum + name: 'sourceRestriction' + description: 'Whether to enforce traffic restrictions based on `sources` field. If the `sources` field is non-empty, then this field must be set to `SOURCE_RESTRICTION_ENABLED`.' + values: + - :SOURCE_RESTRICTION_UNSPECIFIED + - :SOURCE_RESTRICTION_ENABLED + - :SOURCE_RESTRICTION_DISABLED - !ruby/object:Api::Type::NestedObject name: 'egressTo' description: | diff --git a/mmv1/products/accesscontextmanager/ServicePerimeters.yaml b/mmv1/products/accesscontextmanager/ServicePerimeters.yaml index e4fb6867a3ca..2b941289d41a 100644 --- a/mmv1/products/accesscontextmanager/ServicePerimeters.yaml +++ b/mmv1/products/accesscontextmanager/ServicePerimeters.yaml @@ -334,6 +334,21 @@ properties: represent individual user or service account only. is_set: true item_type: Api::Type::String + - !ruby/object:Api::Type::Array + name: 'sources' + description: 'Sources that this EgressPolicy authorizes access from.' + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'accessLevel' + description: 'An AccessLevel resource name that allows resources outside the ServicePerimeter to be accessed from the inside.' + - !ruby/object:Api::Type::Enum + name: 'sourceRestriction' + description: 'Whether to enforce traffic restrictions based on `sources` field. If the `sources` field is non-empty, then this field must be set to `SOURCE_RESTRICTION_ENABLED`.' + values: + - :SOURCE_RESTRICTION_UNSPECIFIED + - :SOURCE_RESTRICTION_ENABLED + - :SOURCE_RESTRICTION_DISABLED - !ruby/object:Api::Type::NestedObject name: 'egressTo' description: | @@ -613,6 +628,21 @@ properties: represent individual user or service account only. item_type: Api::Type::String is_set: true + - !ruby/object:Api::Type::Array + name: 'sources' + description: 'Sources that this EgressPolicy authorizes access from.' + item_type: !ruby/object:Api::Type::NestedObject + properties: + - !ruby/object:Api::Type::String + name: 'accessLevel' + description: 'An AccessLevel resource name that allows resources outside the ServicePerimeter to be accessed from the inside.' + - !ruby/object:Api::Type::Enum + name: 'sourceRestriction' + description: 'Whether to enforce traffic restrictions based on `sources` field. If the `sources` field is non-empty, then this field must be set to `SOURCE_RESTRICTION_ENABLED`.' + values: + - :SOURCE_RESTRICTION_UNSPECIFIED + - :SOURCE_RESTRICTION_ENABLED + - :SOURCE_RESTRICTION_DISABLED - !ruby/object:Api::Type::NestedObject name: 'egressTo' description: | diff --git a/mmv1/templates/terraform/examples/access_context_manager_access_level_with_vpc_network_sources.tf.erb b/mmv1/templates/terraform/examples/access_context_manager_access_level_with_vpc_network_sources.tf.erb new file mode 100644 index 000000000000..d80fe1a6c9ad --- /dev/null +++ b/mmv1/templates/terraform/examples/access_context_manager_access_level_with_vpc_network_sources.tf.erb @@ -0,0 +1,24 @@ +resource "google_compute_network" "vpc_network" { + name = "tf-test" +} + +resource "google_access_context_manager_access_level" "<%= ctx[:primary_resource_id] %>" { + parent = "accessPolicies/${google_access_context_manager_access_policy.access-policy.name}" + name = "accessPolicies/${google_access_context_manager_access_policy.access-policy.name}/accessLevels/<%= ctx[:vars]['access_level_name'] %>" + title = "<%= ctx[:vars]['access_level_name'] %>" + basic { + conditions { + vpc_network_sources { + vpc_subnetwork { + network = "//compute.googleapis.com/${google_compute_network.vpc_network.id}" + vpc_ip_subnetworks = ["20.0.5.0/24"] + } + } + } + } +} + +resource "google_access_context_manager_access_policy" "access-policy" { + parent = "organizations/123456789" + title = "my policy" +} diff --git a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_level_condition_test.go b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_level_condition_test.go index 9b21e7384e73..047e93801469 100644 --- a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_level_condition_test.go +++ b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_level_condition_test.go @@ -22,10 +22,10 @@ func testAccAccessContextManagerAccessLevelCondition_basicTest(t *testing.T) { project := envvar.GetTestProjectFromEnv() serviceAccountName := fmt.Sprintf("tf-test-%s", acctest.RandString(t, 10)) + vpcName := fmt.Sprintf("test-vpc-%s", acctest.RandString(t, 10)) expected := map[string]interface{}{ - "ipSubnetworks": []interface{}{"192.0.4.0/24"}, - "members": []interface{}{"user:test@google.com", "user:test2@google.com", fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", serviceAccountName, project)}, + "members": []interface{}{"user:test@google.com", "user:test2@google.com", fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", serviceAccountName, project)}, "devicePolicy": map[string]interface{}{ "requireCorpOwned": true, "osConstraints": []interface{}{ @@ -35,6 +35,14 @@ func testAccAccessContextManagerAccessLevelCondition_basicTest(t *testing.T) { }, }, "regions": []interface{}{"IT", "US"}, + "vpcNetworkSources": []interface{}{ + map[string]interface{}{ + "vpcSubnetwork": map[string]interface{}{ + "network": fmt.Sprintf("//compute.googleapis.com/projects/%s/global/networks/%s", project, vpcName), + "vpcIpSubnetworks": []interface{}{"20.0.5.0/24"}, + }, + }, + }, } acctest.VcrTest(t, resource.TestCase{ @@ -43,7 +51,7 @@ func testAccAccessContextManagerAccessLevelCondition_basicTest(t *testing.T) { CheckDestroy: testAccCheckAccessContextManagerAccessLevelConditionDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccAccessContextManagerAccessLevelCondition_basic(org, "my policy", "level", serviceAccountName), + Config: testAccAccessContextManagerAccessLevelCondition_basic(org, "my policy", "level", serviceAccountName, vpcName), Check: testAccCheckAccessContextManagerAccessLevelConditionPresent(t, "google_access_context_manager_access_level_condition.access-level-condition", expected), }, }, @@ -111,7 +119,7 @@ func testAccCheckAccessContextManagerAccessLevelConditionDestroyProducer(t *test } } -func testAccAccessContextManagerAccessLevelCondition_basic(org, policyTitle, levelTitleName, saName string) string { +func testAccAccessContextManagerAccessLevelCondition_basic(org, policyTitle, levelTitleName, saName, vpcName string) string { return fmt.Sprintf(` resource "google_access_context_manager_access_policy" "test-access" { parent = "organizations/%s" @@ -139,10 +147,6 @@ resource "google_access_context_manager_access_level" "test-access" { "US", ] } - - conditions { - ip_subnetworks = ["176.0.4.0/24"] - } } lifecycle { @@ -154,9 +158,12 @@ resource "google_service_account" "created-later" { account_id = "%s" } +resource "google_compute_network" "vpc_network" { + name = "%s" +} + resource "google_access_context_manager_access_level_condition" "access-level-condition" { access_level = google_access_context_manager_access_level.test-access.name - ip_subnetworks = ["192.0.4.0/24"] members = ["user:test@google.com", "user:test2@google.com", "serviceAccount:${google_service_account.created-later.email}"] negate = false device_policy { @@ -171,6 +178,13 @@ resource "google_access_context_manager_access_level_condition" "access-level-co "IT", "US", ] + + vpc_network_sources { + vpc_subnetwork { + network = "//compute.googleapis.com/${google_compute_network.vpc_network.id}" + vpc_ip_subnetworks = ["20.0.5.0/24"] + } + } } -`, org, policyTitle, levelTitleName, levelTitleName, saName) +`, org, policyTitle, levelTitleName, levelTitleName, saName, vpcName) } diff --git a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_level_test.go.erb b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_level_test.go.erb index d1de435be63f..fd21c8ed60d7 100644 --- a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_level_test.go.erb +++ b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_level_test.go.erb @@ -19,6 +19,7 @@ import ( func testAccAccessContextManagerAccessLevel_basicTest(t *testing.T) { org := envvar.GetTestOrgFromEnv(t) + vpcName := fmt.Sprintf("test-vpc-%s", acctest.RandString(t, 10)) acctest.VcrTest(t, resource.TestCase{ PreCheck: func() { acctest.AccTestPreCheck(t) }, @@ -26,7 +27,7 @@ func testAccAccessContextManagerAccessLevel_basicTest(t *testing.T) { CheckDestroy: testAccCheckAccessContextManagerAccessLevelDestroyProducer(t), Steps: []resource.TestStep{ { - Config: testAccAccessContextManagerAccessLevel_basic(org, "my policy", "level"), + Config: testAccAccessContextManagerAccessLevel_basic(org, "my policy", "level", vpcName), }, { ResourceName: "google_access_context_manager_access_level.test-access", @@ -114,7 +115,7 @@ func testAccAccessContextManagerAccessLevel_customTest(t *testing.T) { }) } -func testAccAccessContextManagerAccessLevel_basic(org, policyTitle, levelTitleName string) string { +func testAccAccessContextManagerAccessLevel_basic(org, policyTitle, levelTitleName, vpcName string) string { return fmt.Sprintf(` resource "google_access_context_manager_access_policy" "test-access" { parent = "organizations/%s" @@ -133,7 +134,30 @@ resource "google_access_context_manager_access_level" "test-access" { } } } -`, org, policyTitle, levelTitleName, levelTitleName) + +resource "google_compute_network" "vpc_network" { + name = "%s" +} + +resource "google_access_context_manager_access_level" "test-access2" { + parent = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}" + name = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}/accessLevels/%s2" + title = "%s2" + description = "hello2" + basic { + combining_function = "AND" + conditions { + vpc_network_sources { + vpc_subnetwork { + network = "//compute.googleapis.com/${google_compute_network.vpc_network.id}" + vpc_ip_subnetworks = ["20.0.5.0/24"] + } + } + } + } +} + +`, org, policyTitle, levelTitleName, levelTitleName, vpcName, levelTitleName, levelTitleName) } func testAccAccessContextManagerAccessLevel_custom(org, policyTitle, levelTitleName string) string { diff --git a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_levels_test.go b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_levels_test.go index 4098e8683ff3..21e782058d6f 100644 --- a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_levels_test.go +++ b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_access_levels_test.go @@ -17,6 +17,7 @@ import ( func testAccAccessContextManagerAccessLevels_basicTest(t *testing.T) { org := envvar.GetTestOrgFromEnv(t) + vpcName := fmt.Sprintf("test-vpc-%s", acctest.RandString(t, 10)) acctest.VcrTest(t, resource.TestCase{ PreCheck: func() { acctest.AccTestPreCheck(t) }, @@ -32,7 +33,7 @@ func testAccAccessContextManagerAccessLevels_basicTest(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAccessContextManagerAccessLevels_basicUpdated(org, "my new policy", "corpnet_access", "prodnet_access"), + Config: testAccAccessContextManagerAccessLevels_basicUpdated(org, "my new policy", "corpnet_access", "prodnet_access", vpcName), }, { ResourceName: "google_access_context_manager_access_levels.test-access", @@ -116,13 +117,17 @@ resource "google_access_context_manager_access_levels" "test-access" { `, org, policyTitle, levelTitleName1, levelTitleName1, levelTitleName2, levelTitleName2) } -func testAccAccessContextManagerAccessLevels_basicUpdated(org, policyTitle, levelTitleName1, levelTitleName2 string) string { +func testAccAccessContextManagerAccessLevels_basicUpdated(org, policyTitle, levelTitleName1, levelTitleName2, vpcName string) string { return fmt.Sprintf(` resource "google_access_context_manager_access_policy" "test-access" { parent = "organizations/%s" title = "%s" } +resource "google_compute_network" "vpc_network" { + name = "%s" +} + resource "google_access_context_manager_access_levels" "test-access" { parent = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}" @@ -139,17 +144,22 @@ resource "google_access_context_manager_access_levels" "test-access" { } access_levels { - name = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}/accessLevels/%s" - title = "%s" - description = "hello again" - basic { - conditions { - ip_subnetworks = ["176.0.4.0/24"] - } + name = "accessPolicies/${google_access_context_manager_access_policy.test-access.name}/accessLevels/%s" + title = "%s" + description = "hello again" + basic { + conditions { + vpc_network_sources { + vpc_subnetwork { + network = "//compute.googleapis.com/${google_compute_network.vpc_network.id}" + vpc_ip_subnetworks = ["20.0.5.0/24"] + } + } + } } } } -`, org, policyTitle, levelTitleName1, levelTitleName1, levelTitleName2, levelTitleName2) +`, org, policyTitle, vpcName, levelTitleName1, levelTitleName1, levelTitleName2, levelTitleName2) } func testAccAccessContextManagerAccessLevel_empty(org, policyTitle string) string { diff --git a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_egress_policy_test.go b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_egress_policy_test.go index f0556dfceb5c..d6c2db7e0771 100644 --- a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_egress_policy_test.go +++ b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_egress_policy_test.go @@ -110,10 +110,27 @@ resource "google_access_context_manager_service_perimeter_egress_policy" "test-a } +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/level" + title = "level" + description = "hello" + basic { + combining_function = "AND" + conditions { + ip_subnetworks = ["192.0.4.0/24"] + } + } +} + resource "google_access_context_manager_service_perimeter_egress_policy" "test-access2" { perimeter = google_access_context_manager_service_perimeter.test-access.name egress_from { identity_type = "ANY_USER_ACCOUNT" + sources { + access_level = google_access_context_manager_access_level.test-access.name + } + source_restriction = "SOURCE_RESTRICTION_ENABLED" } } diff --git a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_test.go.erb b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_test.go.erb index 42849562162d..ede6c94c328e 100644 --- a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_test.go.erb +++ b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_service_perimeter_test.go.erb @@ -340,6 +340,10 @@ resource "google_access_context_manager_service_perimeter" "test-access" { egress_policies { egress_from { identity_type = "ANY_USER_ACCOUNT" + sources { + access_level = google_access_context_manager_access_level.test-access.name + } + source_restriction = "SOURCE_RESTRICTION_ENABLED" } egress_to { operations { diff --git a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_services_perimeters_test.go b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_services_perimeters_test.go index a220ea5a5e26..01ef093c0d13 100644 --- a/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_services_perimeters_test.go +++ b/mmv1/third_party/terraform/services/accesscontextmanager/resource_access_context_manager_services_perimeters_test.go @@ -136,6 +136,13 @@ resource "google_access_context_manager_service_perimeters" "test-access" { } } } + egress_from { + identity_type = "ANY_USER_ACCOUNT" + sources { + access_level = google_access_context_manager_access_level.test-access.name + } + source_restriction = "SOURCE_RESTRICTION_ENABLED" + } } } }