From 94170b688e6f736fbe7d405adc8c2e96bc55fc55 Mon Sep 17 00:00:00 2001 From: jh629g Date: Wed, 19 Aug 2020 09:41:33 -0500 Subject: [PATCH] Update CMS for cross domain role assignment When a user with a domain different than the domain of the user authorizing heat to create the resources that ranger defines is added to a customer template, heat returns stack validation as the user cannot be found in the domain of the authorizing user. Updated to check if use match auth user and if not, use ranger conf domain as previous Change-Id: I1406b61c695a5d9a3d94e732b95c2b683c94852b --- .../rds/controllers/v1/resources/root.py | 21 +++++++++++-------- .../rds/services/resource.py | 6 +++--- .../rds/services/yaml_customer_builder.py | 16 +++++++++----- .../v1/resources/test_create_resource.py | 20 +++++++++--------- .../unit/rds/services/test_create_resource.py | 2 +- .../unit/rds/services/test_customer_yaml.py | 18 ++++++++++------ 6 files changed, 49 insertions(+), 34 deletions(-) diff --git a/orm/services/resource_distributor/rds/controllers/v1/resources/root.py b/orm/services/resource_distributor/rds/controllers/v1/resources/root.py index cbddbfd8..7a5eb283 100755 --- a/orm/services/resource_distributor/rds/controllers/v1/resources/root.py +++ b/orm/services/resource_distributor/rds/controllers/v1/resources/root.py @@ -183,16 +183,17 @@ class CreateNewResource(rest.RestController): uuid = resource.service_template.tracking.tracking_id resource_type = resource.service_template.resource.resource_type base_url = pecan.request.application_url - user_domain = None + user_data = {} if resource_type == 'customer': - user_domain = pecan.request.headers['User-Domain'] + user_data['user_domain'] = pecan.request.headers['User-Domain'] + user_data['user_id'] = pecan.request.headers['X-RANGER-Client'] jsondata = ast.literal_eval(jsondata) try: resource_id = ResourceService.main(jsondata, uuid, resource_type, 'create', - user_domain) + user_data) site_link = "%s/v1/rds/%s/%s" % (base_url, resource_type, resource_id) @@ -228,16 +229,17 @@ class CreateNewResource(rest.RestController): resource_type = resource.service_template.resource.resource_type base_url = pecan.request.application_url jsondata = ast.literal_eval(jsondata) - user_domain = None + user_data = {} if resource_type == 'customer': - user_domain = pecan.request.headers['User-Domain'] + user_data['user_domain'] = pecan.request.headers['User-Domain'] + user_data['user_id'] = pecan.request.headers['X-RANGER-Client'] try: resource_id = ResourceService.main(jsondata, uuid, resource_type, 'modify', - user_domain) + user_data) my_logger.debug("data sent") site_link = "%s/v1/rds/%s/%s" % (base_url, resource_type, @@ -273,9 +275,10 @@ class CreateNewResource(rest.RestController): jsondata = ast.literal_eval(jsondata) resource_uuid = resource.service_template.tracking.tracking_id resource_type = resource.service_template.resource.resource_type - user_domain = None + user_data = {} if resource_type == 'customer': - user_domain = pecan.request.headers['User-Domain'] + user_data['user_domain'] = pecan.request.headers['User-Domain'] + user_data['user_id'] = pecan.request.headers['X-RANGER-Client'] if resource_type not in resources_operation_list or operation not in \ resources_operation_list[resource_type]: raise NotAllowedError("delete Not allowed for this" @@ -285,7 +288,7 @@ class CreateNewResource(rest.RestController): resource_uuid, resource_type, operation, - user_domain) + user_data) return resource_id except ConflictValue as e: my_logger.error("the request blocked need to wait" diff --git a/orm/services/resource_distributor/rds/services/resource.py b/orm/services/resource_distributor/rds/services/resource.py index 8a540a5b..dd6bee0f 100755 --- a/orm/services/resource_distributor/rds/services/resource.py +++ b/orm/services/resource_distributor/rds/services/resource.py @@ -127,7 +127,7 @@ def _create_template_data(input_data): yamldata = "delete" elif input_data.resource_type == "customer": yamldata = yaml_customer_builder.yamlbuilder(jsondata, target, - input_data.user_domain) + input_data.user_data) elif input_data.resource_type == "group": yamldata = yaml_group_builder.yamlbuilder(jsondata, target) elif input_data.resource_type == "flavor": @@ -264,7 +264,7 @@ def _generate_resource_data(input_data): # User domain is only used in the case that a customer template is being generated, # as certain builds of heat require user_domain in order to validate roles -def main(jsondata, external_transaction_id, resource_type, operation, user_domain=None): +def main(jsondata, external_transaction_id, resource_type, operation, user_data=None): """main function handle resource operation.""" my_logger.info("got %s for %s resource" % (operation, resource_type)) try: @@ -277,7 +277,7 @@ def main(jsondata, external_transaction_id, resource_type, operation, user_domai my_logger.debug("iterate through the regions see if none in submitted") _check_resource_status(input_data) my_logger.debug("get uuid from uuid generator") - input_data.user_domain = user_domain + input_data.user_data = user_data input_data.transaction_id = uuid_utils.get_random_uuid() my_logger.debug("uuid ={}".format(input_data.transaction_id)) # add regions status from rms (to check if it down) diff --git a/orm/services/resource_distributor/rds/services/yaml_customer_builder.py b/orm/services/resource_distributor/rds/services/yaml_customer_builder.py index dbb93eae..6de82104 100755 --- a/orm/services/resource_distributor/rds/services/yaml_customer_builder.py +++ b/orm/services/resource_distributor/rds/services/yaml_customer_builder.py @@ -26,7 +26,7 @@ def _metadata_to_tags(metadata): str(k) + '=' + str(v) for i in metadata for k, v in i.items()) + ']' -def yamlbuilder(alldata, region, user_domain): +def yamlbuilder(alldata, region, user_data): logger.info("building customer yaml") logger.debug("start building flavor yaml for region %s" % region['name']) """build cstomer yaml. @@ -105,10 +105,16 @@ def yamlbuilder(alldata, region, user_domain): 'groups': user_group, 'roles': user_roles}} else: - resources['resources'][user['id']] = \ - {'type': 'OS::Keystone::UserRoleAssignment\n', - 'properties': {'user': (user['id'] + "{%s}" % user_domain), - 'roles': user_roles}} + if user['id'] == user_data['user_id']: + resources['resources'][user['id']] = \ + {'type': 'OS::Keystone::UserRoleAssignment\n', + 'properties': {'user': (user['id'] + "{%s}" % user_data['user_domain']), + 'roles': user_roles}} + else: + resources['resources'][user['id']] = \ + {'type': 'OS::Keystone::UserRoleAssignment\n', + 'properties': {'user': (user['id'] + "{%s}" % domain), + 'roles': user_roles}} # create the output for users outputs['outputs']["%s_id" % user['id']] = \ diff --git a/orm/tests/unit/rds/controllers/v1/resources/test_create_resource.py b/orm/tests/unit/rds/controllers/v1/resources/test_create_resource.py index 64f580b1..c33777fe 100755 --- a/orm/tests/unit/rds/controllers/v1/resources/test_create_resource.py +++ b/orm/tests/unit/rds/controllers/v1/resources/test_create_resource.py @@ -10,7 +10,7 @@ class TestCreateResource(FunctionalTest): @patch.object(root.ResourceService, 'main', return_value="12345") def test_create_resource_success(self, input): """test create resource as it succeed.""" - headers = {'User-Domain': 'default'} + headers = {'User-Domain': 'default', 'X-Ranger-Client': 'user1'} response = self.app.post_json('/v1/rds/resources', good_data, headers=headers) assert response.json['customer']['id'] == '12345' assert response.status_int == 201 @@ -33,7 +33,7 @@ class TestCreateResource(FunctionalTest): side_effect=Exception("general exception")) def test_create_resource_gen_except(self, input): """test creatte resource to catch general exception.""" - headers = {'User-Domain': 'default'} + headers = {'User-Domain': 'default', 'X-Ranger-Client': 'user1'} response = self.app.post_json('/v1/rds/resources', good_data, headers=headers, expect_errors=True) assert response.status_int == 400 @@ -42,7 +42,7 @@ class TestCreateResource(FunctionalTest): side_effect=root.ConflictValue("region")) def test_create_resource_conflict_except(self, input): """test creatte resource to catch ConflictValue exception.""" - headers = {'User-Domain': 'default'} + headers = {'User-Domain': 'default', 'X-Ranger-Client': 'user1'} response = self.app.post_json('/v1/rds/resources', good_data, headers=headers, expect_errors=True) assert response.status_int == 409 @@ -50,7 +50,7 @@ class TestCreateResource(FunctionalTest): @patch.object(root.ResourceService, 'main', return_value="12345") def test_delete_resource_flavor(self, input): """test delete flavor.""" - headers = {'User-Domain': 'default'} + headers = {'User-Domain': 'default', 'X-Ranger-Client': 'user1'} response = self.app.delete_json('/v1/rds/resources', flavor_data, headers=headers) assert response.status_int == 200 @@ -60,7 +60,7 @@ class TestCreateResource(FunctionalTest): """test delete resource not flavor.""" flavor_data["service_template"]["resource"]['resource_type'] = \ "customer" - headers = {'User-Domain': 'default'} + headers = {'User-Domain': 'default', 'X-Ranger-Client': 'user1'} response = self.app.delete_json('/v1/rds/resources', flavor_data, headers=headers, expect_errors=True) assert response.status_int == 405 @@ -70,7 +70,7 @@ class TestCreateResource(FunctionalTest): side_effect=root.ConflictValue("region")) def test_delete_resource_flavor_con(self, input): """test delete flavor while previous process still in progress.""" - headers = {'User-Domain': 'default'} + headers = {'User-Domain': 'default', 'X-Ranger-Client': 'user1'} try: response = self.app.delete_json('/v1/rds/resources', flavor_data, headers=headers) @@ -82,7 +82,7 @@ class TestCreateResource(FunctionalTest): side_effect=Exception("unknown error")) def test_delete_resource_flavor_exce(self, input): """test delete flavor with general; exception.""" - headers = {'User-Domain': 'default'} + headers = {'User-Domain': 'default', 'X-Ranger-Client': 'user1'} try: response = self.app.delete_json('/v1/rds/resources', flavor_data, headers=headers) @@ -94,7 +94,7 @@ class TestCreateResource(FunctionalTest): def test_update_resource_success(self, input): updated = False """test update resource as it succeed.""" - headers = {'User-Domain': 'default'} + headers = {'User-Domain': 'default', 'X-Ranger-Client': 'user1'} response = self.app.put_json('/v1/rds/resources', good_data, headers=headers) if 'updated' in response.json['customer']: @@ -107,7 +107,7 @@ class TestCreateResource(FunctionalTest): side_effect=Exception("unknown error")) def test_put_resource_gen_exce(self, input): """test customer put with general; exception.""" - headers = {'User-Domain': 'default'} + headers = {'User-Domain': 'default', 'X-Ranger-Client': 'user1'} try: response = self.app.put_json('/v1/rds/resources', good_data, headers=headers) @@ -119,7 +119,7 @@ class TestCreateResource(FunctionalTest): side_effect=root.ConflictValue("region")) def test_modify_resource_conflict_except(self, input): """test modify resource to catch ConflictValue exception.""" - headers = {'User-Domain': 'default'} + headers = {'User-Domain': 'default', 'X-Ranger-Client': 'user1'} response = self.app.put_json('/v1/rds/resources', good_data, headers=headers, expect_errors=True) assert response.status_int == 409 diff --git a/orm/tests/unit/rds/services/test_create_resource.py b/orm/tests/unit/rds/services/test_create_resource.py index f34246da..f7994a17 100755 --- a/orm/tests/unit/rds/services/test_create_resource.py +++ b/orm/tests/unit/rds/services/test_create_resource.py @@ -83,7 +83,7 @@ class CreateResource(unittest.TestCase): operation='create', targets=targets ) - input_data.user_domain = 'default' + input_data.user_data = {'user_domain': 'default', 'user_id': 'user1'} status_model = StatusModel(status=[result]) status_model.regions = None result.return_value = status_model diff --git a/orm/tests/unit/rds/services/test_customer_yaml.py b/orm/tests/unit/rds/services/test_customer_yaml.py index b00061c0..a5fe19dd 100755 --- a/orm/tests/unit/rds/services/test_customer_yaml.py +++ b/orm/tests/unit/rds/services/test_customer_yaml.py @@ -287,7 +287,8 @@ class CreateResource(unittest.TestCase): ver = mock_conf.yaml_configs.customer_yaml.yaml_version = '2015-1-1' domain = mock_conf.yaml_configs.customer_yaml.customer_domain = 'default' mock_conf.yaml_configs.customer_yaml.yaml_options.quotas = False - yamlfile = CustomerBuild.yamlbuilder(alldata, region_quotas, user_domain='default') + user_data = {'user_domain': 'default', 'user_id': 'user1'} + yamlfile = CustomerBuild.yamlbuilder(alldata, region_quotas, user_data) yamlfile_as_json = yaml.safe_load(yamlfile) self.assertEqual(yamlfile_as_json['heat_template_version'], ver) self.assertEqual(yaml.safe_load(yamlfile), yaml.safe_load(fullyaml_no_users_quotasoff)) @@ -298,7 +299,8 @@ class CreateResource(unittest.TestCase): ver = mock_conf.yaml_configs.customer_yaml.yaml_version = '2015-1-2' domain = mock_conf.yaml_configs.customer_yaml.customer_domain = 'default' mock_conf.yaml_configs.customer_yaml.yaml_options.quotas = False - yamlfile = CustomerBuild.yamlbuilder(alldata, region_users, user_domain='default') + user_data = {'user_domain': 'default', 'user_id': 'user1'} + yamlfile = CustomerBuild.yamlbuilder(alldata, region_users, user_data) yamlfile_as_json = yaml.safe_load(yamlfile) self.assertEqual(yamlfile_as_json['heat_template_version'], ver) self.assertEqual(yaml.safe_load(yamlfile), yaml.safe_load(fullyaml_with_users_quotasoff)) @@ -309,7 +311,8 @@ class CreateResource(unittest.TestCase): ver = mock_conf.yaml_configs.customer_yaml.yaml_version = '2015-1-1' domain = mock_conf.yaml_configs.customer_yaml.customer_domain = 'default' mock_conf.yaml_configs.customer_yaml.yaml_options.quotas = True - yamlfile = CustomerBuild.yamlbuilder(alldata, region_users, user_domain='default') + user_data = {'user_domain': 'default', 'user_id': 'user1'} + yamlfile = CustomerBuild.yamlbuilder(alldata, region_users, user_data) yamlfile_as_json = yaml.safe_load(yamlfile) self.assertEqual(yamlfile_as_json['heat_template_version'], ver) self.assertEqual(yaml.safe_load(yamlfile), yaml.safe_load(full_yaml_default_quotas)) @@ -320,7 +323,8 @@ class CreateResource(unittest.TestCase): ver = mock_conf.yaml_configs.customer_yaml.yaml_version = '2015-1-1' domain = mock_conf.yaml_configs.customer_yaml.customer_domain = 'default' mock_conf.yaml_configs.customer_yaml.yaml_options.quotas = True - yamlfile = CustomerBuild.yamlbuilder(alldata, region_quotas, user_domain='default') + user_data = {'user_domain': 'default', 'user_id': 'user1'} + yamlfile = CustomerBuild.yamlbuilder(alldata, region_quotas, user_data) yamlfile_as_json = yaml.safe_load(yamlfile) self.assertEqual(yamlfile_as_json['heat_template_version'], ver) self.assertEqual(yaml.safe_load(yamlfile), yaml.safe_load(full_yaml_quotas)) @@ -334,7 +338,8 @@ class CreateResource(unittest.TestCase): mock_conf.authentication.user_domain_name = 'default' mock_conf.yaml_configs.customer_yaml.yaml_options.quotas = False mock_conf.yaml_configs.customer_yaml.yaml_options.type = "ldap" - yamlfile = CustomerBuild.yamlbuilder(alldata, region_users, user_domain='default') + user_data = {'user_domain': 'default', 'user_id': 'user1'} + yamlfile = CustomerBuild.yamlbuilder(alldata, region_users, user_data) yamlfile_as_json = yaml.safe_load(yamlfile) self.assertEqual(yamlfile_as_json['heat_template_version'], ver) @@ -345,7 +350,8 @@ class CreateResource(unittest.TestCase): ver = mock_conf.yaml_configs.customer_yaml.yaml_version = '2015-1-2' domain = mock_conf.yaml_configs.customer_yaml.customer_domain = 'default' mock_conf.yaml_configs.customer_yaml.yaml_options.quotas = True - yamlfile = CustomerBuild.yamlbuilder(alldata, region_users_v4, user_domain='default') + user_data = {'user_domain': 'default', 'user_id': 'user1'} + yamlfile = CustomerBuild.yamlbuilder(alldata, region_users_v4, user_data) yamlfile_as_json = yaml.safe_load(yamlfile) self.assertEqual(yamlfile_as_json['heat_template_version'], ver) self.assertEqual(yaml.safe_load(yamlfile), yaml.safe_load(fullyaml_aic4))