From d44ed7f18cac6bd7c44a0cba5ca2bf4d6ce8c4d4 Mon Sep 17 00:00:00 2001 From: Harry Rybacki Date: Mon, 4 Jun 2018 22:19:58 -0400 Subject: [PATCH] Ensure default roles created during bootstrap Expand bootstrap process to include creation of roles outlined in basic default roles spec. The bootstrap process now creates two new roles, 'reader' and 'member, in addition to the well established 'admin' role. During this process, a role implication[1] chain is created: 'admin' implies 'member' and 'member' implies 'reader'. [1] - https://developer.openstack.org/api-ref/identity/v3/#create-role-inference-rule Co-Authored-By: Juan Antonio Osorio Robles bp basic-default-roles Depends-On: https://review.openstack.org/574149 Change-Id: Ie18a269e3d1075d955fe494acaf634a393c6bd7b --- keystone/cmd/bootstrap.py | 75 ++++++++++++++----- keystone/cmd/cli.py | 2 + keystone/tests/unit/test_cli.py | 12 ++- ...-basic-default-roles-4ff6502b6ac57d48.yaml | 15 ++++ 4 files changed, 80 insertions(+), 24 deletions(-) create mode 100644 releasenotes/notes/bp-basic-default-roles-4ff6502b6ac57d48.yaml diff --git a/keystone/cmd/bootstrap.py b/keystone/cmd/bootstrap.py index 66ac7626eb..2d1ec8577b 100644 --- a/keystone/cmd/bootstrap.py +++ b/keystone/cmd/bootstrap.py @@ -36,6 +36,12 @@ class Bootstrapper(object): self.project_id = None self.project_name = None + self.reader_role_id = None + self.reader_role_name = 'reader' + + self.member_role_id = None + self.member_role_name = 'member' + self.admin_role_id = None self.admin_role_name = None @@ -55,6 +61,8 @@ class Bootstrapper(object): self._bootstrap_default_domain() self._bootstrap_project() self._bootstrap_admin_user() + self._bootstrap_reader_role() + self._bootstrap_member_role() self._bootstrap_admin_role() self._bootstrap_project_role_assignment() self._bootstrap_system_role_assignment() @@ -101,6 +109,53 @@ class Bootstrapper(object): self.project_id = project['id'] + def _ensure_role_exists(self, role_name): + # NOTE(morganfainberg): Do not create the role if it already exists. + try: + role_id = uuid.uuid4().hex + role = {'name': role_name, 'id': role_id} + role = PROVIDERS.role_api.create_role(role_id, role) + LOG.info('Created role %s', role_name) + return role + except exception.Conflict: + LOG.info('Role %s exists, skipping creation.', role_name) + # NOTE(davechen): There is no backend method to get the role + # by name, so build the hints to list the roles and filter by + # name instead. + hints = driver_hints.Hints() + hints.add_filter('name', role_name) + return PROVIDERS.role_api.list_roles(hints)[0] + + def _ensure_implied_role(self, prior_role_id, implied_role_id): + try: + PROVIDERS.role_api.create_implied_role(prior_role_id, + implied_role_id) + LOG.info( + 'Created implied role where %s implies %s', + prior_role_id, + implied_role_id + ) + except exception.Conflict: + LOG.info( + 'Implied role where %s implies %s exists, skipping creation.', + prior_role_id, + implied_role_id + ) + + def _bootstrap_reader_role(self): + role = self._ensure_role_exists(self.reader_role_name) + self.reader_role_id = role['id'] + + def _bootstrap_member_role(self): + role = self._ensure_role_exists(self.member_role_name) + self.member_role_id = role['id'] + self._ensure_implied_role(self.member_role_id, self.reader_role_id) + + def _bootstrap_admin_role(self): + role = self._ensure_role_exists(self.admin_role_name) + self.admin_role_id = role['id'] + self._ensure_implied_role(self.admin_role_id, self.member_role_id) + def _bootstrap_admin_user(self): # NOTE(morganfainberg): Do not create the user if it already exists. try: @@ -156,26 +211,6 @@ class Bootstrapper(object): self.admin_user_id = user['id'] - def _bootstrap_admin_role(self): - # NOTE(morganfainberg): Do not create the role if it already exists. - try: - role_id = uuid.uuid4().hex - role = {'name': self.admin_role_name, 'id': role_id} - role = PROVIDERS.role_api.create_role(role_id, role) - LOG.info('Created role %s', self.admin_role_name) - except exception.Conflict: - LOG.info( - 'Role %s exists, skipping creation.', self.admin_role_name - ) - # NOTE(davechen): There is no backend method to get the role - # by name, so build the hints to list the roles and filter by - # name instead. - hints = driver_hints.Hints() - hints.add_filter('name', self.admin_role_name) - role = PROVIDERS.role_api.list_roles(hints)[0] - - self.admin_role_id = role['id'] - def _bootstrap_project_role_assignment(self): try: PROVIDERS.assignment_api.add_role_to_user_and_project( diff --git a/keystone/cmd/cli.py b/keystone/cmd/cli.py index f1d8a4d647..0fb54fd152 100644 --- a/keystone/cmd/cli.py +++ b/keystone/cmd/cli.py @@ -167,6 +167,8 @@ class BootStrap(BaseApp): self.bootstrapper.region_id = self.region_id self.bootstrapper.bootstrap() + self.reader_role_id = self.bootstrapper.reader_role_id + self.member_role_id = self.bootstrapper.member_role_id self.role_id = self.bootstrapper.admin_role_id self.project_id = self.bootstrapper.project_id diff --git a/keystone/tests/unit/test_cli.py b/keystone/tests/unit/test_cli.py index fb4df0d4c4..210c65bb90 100644 --- a/keystone/tests/unit/test_cli.py +++ b/keystone/tests/unit/test_cli.py @@ -107,20 +107,24 @@ class CliBootStrapTestCase(unit.SQLDriverOverrides, unit.TestCase): user = PROVIDERS.identity_api.get_user_by_name( bootstrap.username, 'default') - role = PROVIDERS.role_api.get_role(bootstrap.role_id) + admin_role = PROVIDERS.role_api.get_role(bootstrap.role_id) + reader_role = PROVIDERS.role_api.get_role(bootstrap.reader_role_id) + member_role = PROVIDERS.role_api.get_role(bootstrap.member_role_id) role_list = ( PROVIDERS.assignment_api.get_roles_for_user_and_project( user['id'], project['id'])) - self.assertIs(1, len(role_list)) - self.assertEqual(role_list[0], role['id']) + self.assertIs(3, len(role_list)) + self.assertIn(admin_role['id'], role_list) + self.assertIn(reader_role['id'], role_list) + self.assertIn(member_role['id'], role_list) system_roles = ( PROVIDERS.assignment_api.list_system_grants_for_user( user['id'] ) ) self.assertIs(1, len(system_roles)) - self.assertEqual(system_roles[0]['id'], role['id']) + self.assertEqual(system_roles[0]['id'], admin_role['id']) # NOTE(morganfainberg): Pass an empty context, it isn't used by # `authenticate` method. PROVIDERS.identity_api.authenticate( diff --git a/releasenotes/notes/bp-basic-default-roles-4ff6502b6ac57d48.yaml b/releasenotes/notes/bp-basic-default-roles-4ff6502b6ac57d48.yaml new file mode 100644 index 0000000000..d47a822621 --- /dev/null +++ b/releasenotes/notes/bp-basic-default-roles-4ff6502b6ac57d48.yaml @@ -0,0 +1,15 @@ +--- +features: + - | + [`blueprint basic-default-roles `_] + Support has been added for deploying two new roles during the bootstrap + process, `reader` and `member`, in addition to the `admin` role. +upgrades: + - | + If the bootstrap process is re-run, and a `reader`, `member`, or `admin` + role already exists, a role implication chain will be created: `admin` + implies `member` implies `reader`. If you do not want these role + implications either skip running bootstrap or delete them after it has + completed execution. See + [`blueprint basic-default-roles `_] + for more details.