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 <jaosorior@redhat.com>
bp basic-default-roles
Depends-On: https://review.openstack.org/574149
Change-Id: Ie18a269e3d1075d955fe494acaf634a393c6bd7b
This commit is contained in:
Harry Rybacki 2018-06-04 22:19:58 -04:00
parent 104717d458
commit d44ed7f18c
4 changed files with 80 additions and 24 deletions

View File

@ -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(

View File

@ -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

View File

@ -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(

View File

@ -0,0 +1,15 @@
---
features:
- |
[`blueprint basic-default-roles <https://blueprints.launchpad.net/keystone/+spec/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 <https://blueprints.launchpad.net/keystone/+spec/basic-default-roles>`_]
for more details.