From 33bced6f99b844cd25d733007f2dc0df470c4b9e Mon Sep 17 00:00:00 2001 From: Rajesh K Pirati Date: Wed, 22 Dec 2021 20:17:57 +0530 Subject: [PATCH] Authorization policy to support any service specific attributes --- ...ource_ibm_iam_authorization.policy_test.go | 71 ++++ ibm/resource_ibm_iam_authorization_policy.go | 351 ++++++++++++------ .../r/iam_authorization_policy.html.markdown | 72 +++- 3 files changed, 379 insertions(+), 115 deletions(-) diff --git a/ibm/resource_ibm_iam_authorization.policy_test.go b/ibm/resource_ibm_iam_authorization.policy_test.go index 7541d9dd61e..b2e7043e909 100644 --- a/ibm/resource_ibm_iam_authorization.policy_test.go +++ b/ibm/resource_ibm_iam_authorization.policy_test.go @@ -129,6 +129,27 @@ func TestAccIBMIAMAuthorizationPolicyDelegatorRole(t *testing.T) { }) } +func TestAccIBMIAMAuthorizationPolicy_ResourceAttributes(t *testing.T) { + var conf iampolicymanagementv1.Policy + sServiceInstance := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + tServiceInstance := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckIBMIAMAuthorizationPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMIAMAuthorizationPolicyResourceAttributes(sServiceInstance, tServiceInstance, tg_cross_network_account_id, tg_cross_network_account_id), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMIAMAuthorizationPolicyExists("ibm_iam_authorization_policy.policy", conf), + resource.TestCheckResourceAttrSet("ibm_iam_authorization_policy.policy", "id"), + ), + }, + }, + }) +} + func testAccCheckIBMIAMAuthorizationPolicyDestroy(s *terraform.State) error { iamPolicyManagementClient, err := testAccProvider.Meta().(ClientSession).IAMPolicyManagementV1API() if err != nil { @@ -264,3 +285,53 @@ func testAccCheckIBMIAMAuthorizationPolicyResourceGroup(sResourceGroup, tResourc `, sResourceGroup, tResourceGroup) } + +func testAccCheckIBMIAMAuthorizationPolicyResourceAttributes(sServiceInstance, tServiceInstance, sAccountID, tAccountID string) string { + + return fmt.Sprintf(` + + resource "ibm_resource_instance" "cos" { + name = "%s" + service = "cloud-object-storage" + plan = "lite" + location = "global" + } + + resource "ibm_resource_instance" "kms" { + name = "%s" + service = "kms" + plan = "tiered-pricing" + location = "us-south" + } + + resource "ibm_iam_authorization_policy" "policy" { + roles = ["Reader"] + + subject_attributes { + name = "accountId" + value = "%s" + } + subject_attributes { + name = "serviceInstance" + value = ibm_resource_instance.cos.id + } + subject_attributes { + name = "serviceName" + value = "cloud-object-storage" + } + + resource_attributes { + name = "serviceName" + value = "kms" + } + resource_attributes { + name = "accountId" + value = "%s" + } + resource_attributes { + name = "serviceInstance" + value = ibm_resource_instance.kms.id + } + } + `, sServiceInstance, tServiceInstance, sAccountID, tAccountID) +} diff --git a/ibm/resource_ibm_iam_authorization_policy.go b/ibm/resource_ibm_iam_authorization_policy.go index 49035f5fce2..51e9d6378bc 100644 --- a/ibm/resource_ibm_iam_authorization_policy.go +++ b/ibm/resource_ibm_iam_authorization_policy.go @@ -24,17 +24,21 @@ func resourceIBMIAMAuthorizationPolicy() *schema.Resource { Schema: map[string]*schema.Schema{ "source_service_name": { - Type: schema.TypeString, - Required: true, - Description: "The source service name", - ForceNew: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ExactlyOneOf: []string{"source_service_name", "subject_attributes"}, + Description: "The source service name", + ForceNew: true, }, "target_service_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "The target service name", + Type: schema.TypeString, + Optional: true, + Computed: true, + ExactlyOneOf: []string{"target_service_name", "resource_attributes"}, + ForceNew: true, + Description: "The target service name", }, "roles": { @@ -45,53 +49,118 @@ func resourceIBMIAMAuthorizationPolicy() *schema.Resource { }, "source_resource_instance_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Description: "The source resource instance Id", + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"subject_attributes"}, + Description: "The source resource instance Id", }, "target_resource_instance_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Description: "The target resource instance Id", + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"resource_attributes"}, + Description: "The target resource instance Id", }, "source_resource_group_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Description: "The source resource group Id", + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"subject_attributes"}, + Description: "The source resource group Id", }, "target_resource_group_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Description: "The target resource group Id", + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"resource_attributes"}, + Description: "The target resource group Id", }, "source_resource_type": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Description: "Resource type of source service", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + ConflictsWith: []string{"subject_attributes"}, + Description: "Resource type of source service", }, "target_resource_type": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Description: "Resource type of target service", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Computed: true, + ConflictsWith: []string{"resource_attributes"}, + Description: "Resource type of target service", }, "source_service_account": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - Description: "Account GUID of source service", + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"subject_attributes"}, + Description: "Account GUID of source service", + }, + + "subject_attributes": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + ForceNew: true, + Description: "Set subject attributes.", + ConflictsWith: []string{"source_resource_instance_id", "source_resource_group_id", "source_resource_type", "source_service_account"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Name of attribute.", + }, + "value": { + Type: schema.TypeString, + Required: true, + Description: "Value of attribute.", + }, + }, + }, + }, + + "resource_attributes": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + ForceNew: true, + Description: "Set resource attributes.", + ConflictsWith: []string{"target_resource_instance_id", "target_resource_group_id", "target_resource_type"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "Name of attribute.", + }, + "value": { + Type: schema.TypeString, + Required: true, + Description: "Value of attribute.", + }, + "operator": { + Type: schema.TypeString, + Optional: true, + Default: "stringEquals", + Description: "Operator of attribute.", + }, + }, + }, }, "version": { @@ -109,9 +178,11 @@ func resourceIBMIAMAuthorizationPolicy() *schema.Resource { } func resourceIBMIAMAuthorizationPolicyCreate(d *schema.ResourceData, meta interface{}) error { - sourceServiceName := d.Get("source_service_name").(string) - targetServiceName := d.Get("target_service_name").(string) + + var sourceServiceName, targetServiceName string policyType := "authorization" + policySubject := &iampolicymanagementv1.PolicySubject{} + policyResource := &iampolicymanagementv1.PolicyResource{} userDetails, err := meta.(ClientSession).BluemixUserDetails() if err != nil { @@ -123,87 +194,125 @@ func resourceIBMIAMAuthorizationPolicyCreate(d *schema.ResourceData, meta interf return err } - sourceServiceAccount := userDetails.userAccount + // check subject_attributes exists + if attributes, ok := d.GetOk("subject_attributes"); ok { + for _, attribute := range attributes.(*schema.Set).List() { + a := attribute.(map[string]interface{}) + name := a["name"].(string) + value := a["value"].(string) + if name == "serviceName" { + sourceServiceName = value + } + at := iampolicymanagementv1.SubjectAttribute{ + Name: &name, + Value: &value, + } + policySubject.Attributes = append(policySubject.Attributes, at) + } + } else { - if account, ok := d.GetOk("source_service_account"); ok { - sourceServiceAccount = account.(string) - } + sourceServiceName = d.Get("source_service_name").(string) - accountIdSubjectAttribute := &iampolicymanagementv1.SubjectAttribute{ - Name: core.StringPtr("accountId"), - Value: &sourceServiceAccount, - } - serviceNameSubjectAttribute := &iampolicymanagementv1.SubjectAttribute{ - Name: core.StringPtr("serviceName"), - Value: &sourceServiceName, - } + serviceNameSubjectAttribute := &iampolicymanagementv1.SubjectAttribute{ + Name: core.StringPtr("serviceName"), + Value: &sourceServiceName, + } + policySubject.Attributes = append(policySubject.Attributes, *serviceNameSubjectAttribute) - policySubject := &iampolicymanagementv1.PolicySubject{ - Attributes: []iampolicymanagementv1.SubjectAttribute{*accountIdSubjectAttribute, *serviceNameSubjectAttribute}, - } + sourceServiceAccount := userDetails.userAccount + if account, ok := d.GetOk("source_service_account"); ok { + sourceServiceAccount = account.(string) + } - accountIDResourceAttribute := &iampolicymanagementv1.ResourceAttribute{ - Name: core.StringPtr("accountId"), - Value: core.StringPtr(userDetails.userAccount), - Operator: core.StringPtr("stringEquals"), - } + accountIdSubjectAttribute := &iampolicymanagementv1.SubjectAttribute{ + Name: core.StringPtr("accountId"), + Value: &sourceServiceAccount, + } - serviceNameResourceAttribute := &iampolicymanagementv1.ResourceAttribute{ - Name: core.StringPtr("serviceName"), - Value: core.StringPtr(targetServiceName), - Operator: core.StringPtr("stringEquals"), - } + policySubject.Attributes = append(policySubject.Attributes, *accountIdSubjectAttribute) - policyResource := &iampolicymanagementv1.PolicyResource{ - Attributes: []iampolicymanagementv1.ResourceAttribute{*accountIDResourceAttribute, *serviceNameResourceAttribute}, - } + if sID, ok := d.GetOk("source_resource_instance_id"); ok { + serviceInstanceSubjectAttribute := iampolicymanagementv1.SubjectAttribute{ + Name: core.StringPtr("serviceInstance"), + Value: core.StringPtr(sID.(string)), + } + policySubject.Attributes = append(policySubject.Attributes, serviceInstanceSubjectAttribute) + } - if sID, ok := d.GetOk("source_resource_instance_id"); ok { - serviceInstanceSubjectAttribute := iampolicymanagementv1.SubjectAttribute{ - Name: core.StringPtr("serviceInstance"), - Value: core.StringPtr(sID.(string)), + if sType, ok := d.GetOk("source_resource_type"); ok { + resourceTypeSubjectAttribute := iampolicymanagementv1.SubjectAttribute{ + Name: core.StringPtr("resourceType"), + Value: core.StringPtr(sType.(string)), + } + policySubject.Attributes = append(policySubject.Attributes, resourceTypeSubjectAttribute) } - policySubject.Attributes = append(policySubject.Attributes, serviceInstanceSubjectAttribute) - } - if tID, ok := d.GetOk("target_resource_instance_id"); ok { - serviceInstanceResourceAttribute := iampolicymanagementv1.ResourceAttribute{ - Name: core.StringPtr("serviceInstance"), - Value: core.StringPtr(tID.(string)), + if sResGrpID, ok := d.GetOk("source_resource_group_id"); ok { + resourceGroupSubjectAttribute := iampolicymanagementv1.SubjectAttribute{ + Name: core.StringPtr("resourceGroupId"), + Value: core.StringPtr(sResGrpID.(string)), + } + policySubject.Attributes = append(policySubject.Attributes, resourceGroupSubjectAttribute) } - policyResource.Attributes = append(policyResource.Attributes, serviceInstanceResourceAttribute) } - if sType, ok := d.GetOk("source_resource_type"); ok { - resourceTypeSubjectAttribute := iampolicymanagementv1.SubjectAttribute{ - Name: core.StringPtr("resourceType"), - Value: core.StringPtr(sType.(string)), + // check resource_attributes exists + if attributes, ok := d.GetOk("resource_attributes"); ok { + for _, attribute := range attributes.(*schema.Set).List() { + a := attribute.(map[string]interface{}) + name := a["name"].(string) + value := a["value"].(string) + operator := a["operator"].(string) + if name == "serviceName" { + targetServiceName = value + } + at := iampolicymanagementv1.ResourceAttribute{ + Name: &name, + Value: &value, + Operator: &operator, + } + policyResource.Attributes = append(policyResource.Attributes, at) } - policySubject.Attributes = append(policySubject.Attributes, resourceTypeSubjectAttribute) - } + } else { + targetServiceName = d.Get("target_service_name").(string) + serviceNameResourceAttribute := &iampolicymanagementv1.ResourceAttribute{ + Name: core.StringPtr("serviceName"), + Value: core.StringPtr(targetServiceName), + Operator: core.StringPtr("stringEquals"), + } + policyResource.Attributes = append(policyResource.Attributes, *serviceNameResourceAttribute) - if tType, ok := d.GetOk("target_resource_type"); ok { - resourceTypeResourceAttribute := iampolicymanagementv1.ResourceAttribute{ - Name: core.StringPtr("resourceType"), - Value: core.StringPtr(tType.(string)), + accountIDResourceAttribute := &iampolicymanagementv1.ResourceAttribute{ + Name: core.StringPtr("accountId"), + Value: core.StringPtr(userDetails.userAccount), + Operator: core.StringPtr("stringEquals"), } - policyResource.Attributes = append(policyResource.Attributes, resourceTypeResourceAttribute) - } - if sResGrpID, ok := d.GetOk("source_resource_group_id"); ok { - resourceGroupSubjectAttribute := iampolicymanagementv1.SubjectAttribute{ - Name: core.StringPtr("resourceGroupId"), - Value: core.StringPtr(sResGrpID.(string)), + policyResource.Attributes = append(policyResource.Attributes, *accountIDResourceAttribute) + + if tID, ok := d.GetOk("target_resource_instance_id"); ok { + serviceInstanceResourceAttribute := iampolicymanagementv1.ResourceAttribute{ + Name: core.StringPtr("serviceInstance"), + Value: core.StringPtr(tID.(string)), + } + policyResource.Attributes = append(policyResource.Attributes, serviceInstanceResourceAttribute) + } + + if tType, ok := d.GetOk("target_resource_type"); ok { + resourceTypeResourceAttribute := iampolicymanagementv1.ResourceAttribute{ + Name: core.StringPtr("resourceType"), + Value: core.StringPtr(tType.(string)), + } + policyResource.Attributes = append(policyResource.Attributes, resourceTypeResourceAttribute) } - policySubject.Attributes = append(policySubject.Attributes, resourceGroupSubjectAttribute) - } - if tResGrpID, ok := d.GetOk("target_resource_group_id"); ok { - resourceGroupResourceAttribute := iampolicymanagementv1.ResourceAttribute{ - Name: core.StringPtr("resourceGroupId"), - Value: core.StringPtr(tResGrpID.(string)), + if tResGrpID, ok := d.GetOk("target_resource_group_id"); ok { + resourceGroupResourceAttribute := iampolicymanagementv1.ResourceAttribute{ + Name: core.StringPtr("resourceGroupId"), + Value: core.StringPtr(tResGrpID.(string)), + } + policyResource.Attributes = append(policyResource.Attributes, resourceGroupResourceAttribute) } - policyResource.Attributes = append(policyResource.Attributes, resourceGroupResourceAttribute) } listRoleOptions := &iampolicymanagementv1.ListRolesOptions{ @@ -271,19 +380,24 @@ func resourceIBMIAMAuthorizationPolicyRead(d *schema.ResourceData, meta interfac d.Set("roles", roles) source := authorizationPolicy.Subjects[0] target := authorizationPolicy.Resources[0] - d.Set("source_service_name", getSubjectAttribute("serviceName", source)) + + d.Set("resource_attributes", setAuthorizationResourceAttributes(target)) + d.Set("target_resource_instance_id", getResourceAttribute("serviceInstance", target)) + d.Set("target_resource_type", getResourceAttribute("resourceType", target)) + d.Set("target_resource_group_id", getResourceAttribute("resourceGroupId", target)) d.Set("target_service_name", getResourceAttribute("serviceName", target)) + + d.Set("subject_attributes", setAuthorizationSubjectAttributes(source)) + d.Set("source_service_name", getSubjectAttribute("serviceName", source)) d.Set("source_resource_instance_id", getSubjectAttribute("serviceInstance", source)) - d.Set("target_resource_instance_id", getResourceAttribute("serviceInstance", target)) d.Set("source_resource_type", getSubjectAttribute("resourceType", source)) - d.Set("target_resource_type", getResourceAttribute("resourceType", target)) d.Set("source_service_account", getSubjectAttribute("accountId", source)) d.Set("source_resource_group_id", getSubjectAttribute("resourceGroupId", source)) - d.Set("target_resource_group_id", getResourceAttribute("resourceGroupId", target)) + return nil } -// Returns nil, because ibmcloud iam cli authoirization policy does not have an update command +// Returns nil, because ibmcloud iam cli authorization policy does not have an update command func resourceIBMIAMAuthorizationPolicyUpdate(d *schema.ResourceData, meta interface{}) error { return nil } @@ -333,3 +447,28 @@ func resourceIBMIAMAuthorizationPolicyExists(d *schema.ResourceData, meta interf return *authorizationPolicy.ID == d.Id(), nil } + +func setAuthorizationResourceAttributes(list iampolicymanagementv1.PolicyResource) []map[string]interface{} { + result := make([]map[string]interface{}, 0) + for _, attribute := range list.Attributes { + l := map[string]interface{}{ + "name": attribute.Name, + "value": attribute.Value, + "operator": attribute.Operator, + } + result = append(result, l) + } + return result +} + +func setAuthorizationSubjectAttributes(list iampolicymanagementv1.PolicySubject) []map[string]interface{} { + result := make([]map[string]interface{}, 0) + for _, attribute := range list.Attributes { + l := map[string]interface{}{ + "name": attribute.Name, + "value": attribute.Value, + } + result = append(result, l) + } + return result +} diff --git a/website/docs/r/iam_authorization_policy.html.markdown b/website/docs/r/iam_authorization_policy.html.markdown index d0d46a3418c..26a9f9d1139 100644 --- a/website/docs/r/iam_authorization_policy.html.markdown +++ b/website/docs/r/iam_authorization_policy.html.markdown @@ -93,20 +93,74 @@ resource "ibm_iam_authorization_policy" "policy" { ``` +``` +### Authorization policy between two specific services. + +```terraform + +resource "ibm_iam_authorization_policy" "policy" { + roles = [ + "Reader", + ] + + resource_attributes { + name = "accountId" + operator = "stringEquals" + value = "12345" + } + resource_attributes { + name = "serviceName" + operator = "stringEquals" + value = "internet-svcs" + } + + resource_attributes { + name = "cfgType" + value = "reliability" + } + + subject_attributes { + name = "accountId" + value = "12345" + } + subject_attributes { + name = "serviceName" + value = "cloudcerts" + } +} + +``` +If user wants to add any resource specific attributes, for example `cfgType` +specific to a service `internet-svcs` use above `resource_attributes` format.
+**Note**: The serviceName and accountId attributes are required for both resource and subject in authorization + ## Argument reference Review the argument references that you can specify for your resource. - `description` (Optional, String) The description of the Authorization Policy. - `roles` - (Required, list) The comma separated list of roles. For more information, about supported service specific roles, see [IAM roles and actions](https://cloud.ibm.com/docs/account?topic=account-iam-service-roles-actions) -- `source_service_account` - (Optional, Forces new resource, string) The account GUID of source service. -- `source_service_name` - (Required, Forces new resource, string) The source service name. -- `target_service_name` - (Required, Forces new resource, string) The target service name. -- `source_resource_instance_id` - (Optional, Forces new resource, string) The source resource instance id. -- `target_resource_instance_id` - (Optional, Forces new resource, string) The target resource instance id. -- `source_resource_type` - (Optional, Forces new resource, string) The resource type of source service. -- `target_resource_type` - (Optional, Forces new resource, string) The resource type of target service. -- `source_resource_group_id` - (Optional, Forces new resource, string) The source resource group id. -- `target_resource_group_id` - (Optional, Forces new resource, string) The target resource group id. +- `source_service_account` - (Optional, Forces new resource, string) The account GUID of source service.**Note** Conflicts with `subject_attributes`. +- `source_service_name` - (Required, Forces new resource, string) The source service name.**Note** Conflicts with `subject_attributes`. +- `target_service_name` - (Required, Forces new resource, string) The target service name.**Note** Conflicts with `resource_attributes`. +- `source_resource_instance_id` - (Optional, Forces new resource, string) The source resource instance id.**Note** Conflicts with `subject_attributes`. +- `target_resource_instance_id` - (Optional, Forces new resource, string) The target resource instance id.**Note** Conflicts with `resource_attributes`. +- `source_resource_type` - (Optional, Forces new resource, string) The resource type of source service.**Note** Conflicts with `subject_attributes`. +- `target_resource_type` - (Optional, Forces new resource, string) The resource type of target service.**Note** Conflicts with `resource_attributes`. +- `source_resource_group_id` - (Optional, Forces new resource, string) The source resource group id.**Note** Conflicts with `subject_attributes`. +- `target_resource_group_id` - (Optional, Forces new resource, string) The target resource group id.**Note** Conflicts with `resource_attributes`. +- `resource_attributes` - (Optional, Forces new resource, list) A nested block describing the resource attributes of this policy.**Note** Conflicts with `target_resource_instance_id`, `target_resource_group_id` and `target_resource_type`. + + Nested scheme for `resource_attributes`: + - `name` - (Required, String) The name of an attribute. Supported values are `serviceName` , `serviceInstance` ,`resourceType` , `resourceGroupId` `accountId` and other service specific resource attributes. + - `value` - (Required, String) The value of an attribute. + - `operator` - (Optional, String) Operator of an attribute. The default value is `stringEquals`. + +- `subject_attributes` - (Optional, Forces new resource, list) A nested block describing the subject attributes of this policy.**Note** Conflicts with `source_resource_instance_id`, `source_resource_group_id` `source_resource_type` and `source_service_account`. + + Nested scheme for `subject_attributes`: + - `name` - (Required, String) The name of an attribute. Supported values are `serviceName` , `serviceInstance` , `region` , `resource` , `resourceType` , `resourceGroupId` `accountId`. + - `value` - (Required, String) The value of an attribute. + - `operator` - (Optional, String) Operator of an attribute. The default value is `stringEquals`. ## Attribute reference In addition to all argument reference list, you can access the following attribute reference after your resource is created.