Merge next branch

This commit is contained in:
Corey Bryant 2015-04-01 15:15:49 +00:00
commit 4d99449780
6 changed files with 161 additions and 42 deletions

View File

@ -78,10 +78,10 @@ options:
default: 'Admin' default: 'Admin'
type: string type: string
description: 'Admin role to be associated with admin and service users' description: 'Admin role to be associated with admin and service users'
token-expiry: token-expiration:
default: "2017-02-05T00:00" default: 3600
type: string type: int
description: "Expiration date of generated admin tokens" description: "Amount of time a token should remain valid (in seconds)."
service-tenant: service-tenant:
default: "services" default: "services"
type: string type: string

View File

@ -202,6 +202,7 @@ class KeystoneContext(context.OSContextGenerator):
ctxt['debug'] = debug and bool_from_string(debug) ctxt['debug'] = debug and bool_from_string(debug)
verbose = config('verbose') verbose = config('verbose')
ctxt['verbose'] = verbose and bool_from_string(verbose) ctxt['verbose'] = verbose and bool_from_string(verbose)
ctxt['token_expiration'] = config('token-expiration')
ctxt['identity_backend'] = config('identity-backend') ctxt['identity_backend'] = config('identity-backend')
ctxt['assignment_backend'] = config('assignment-backend') ctxt['assignment_backend'] = config('assignment-backend')

View File

@ -472,12 +472,14 @@ def create_service_entry(service_name, service_type, service_desc, owner=None):
token=get_admin_token()) token=get_admin_token())
for service in [s._info for s in manager.api.services.list()]: for service in [s._info for s in manager.api.services.list()]:
if service['name'] == service_name: if service['name'] == service_name:
log("Service entry for '%s' already exists." % service_name) log("Service entry for '%s' already exists." % service_name,
level=DEBUG)
return return
manager.api.services.create(name=service_name, manager.api.services.create(name=service_name,
service_type=service_type, service_type=service_type,
description=service_desc) description=service_desc)
log("Created new service entry '%s'" % service_name) log("Created new service entry '%s'" % service_name, level=DEBUG)
def create_endpoint_template(region, service, publicurl, adminurl, def create_endpoint_template(region, service, publicurl, adminurl,
@ -510,7 +512,8 @@ def create_endpoint_template(region, service, publicurl, adminurl,
publicurl=publicurl, publicurl=publicurl,
adminurl=adminurl, adminurl=adminurl,
internalurl=internalurl) internalurl=internalurl)
log("Created new endpoint template for '%s' in '%s'" % (region, service)) log("Created new endpoint template for '%s' in '%s'" % (region, service),
level=DEBUG)
def create_tenant(name): def create_tenant(name):
@ -522,9 +525,21 @@ def create_tenant(name):
if not tenants or name not in [t['name'] for t in tenants]: if not tenants or name not in [t['name'] for t in tenants]:
manager.api.tenants.create(tenant_name=name, manager.api.tenants.create(tenant_name=name,
description='Created by Juju') description='Created by Juju')
log("Created new tenant: %s" % name) log("Created new tenant: %s" % name, level=DEBUG)
return return
log("Tenant '%s' already exists." % name)
log("Tenant '%s' already exists." % name, level=DEBUG)
def user_exists(name):
import manager
manager = manager.KeystoneManager(endpoint=get_local_endpoint(),
token=get_admin_token())
users = [u._info for u in manager.api.users.list()]
if not users or name not in [u['name'] for u in users]:
return False
return True
def create_user(name, password, tenant): def create_user(name, password, tenant):
@ -532,18 +547,20 @@ def create_user(name, password, tenant):
import manager import manager
manager = manager.KeystoneManager(endpoint=get_local_endpoint(), manager = manager.KeystoneManager(endpoint=get_local_endpoint(),
token=get_admin_token()) token=get_admin_token())
users = [u._info for u in manager.api.users.list()] if user_exists(name):
if not users or name not in [u['name'] for u in users]: log("A user named '%s' already exists" % name, level=DEBUG)
return
tenant_id = manager.resolve_tenant_id(tenant) tenant_id = manager.resolve_tenant_id(tenant)
if not tenant_id: if not tenant_id:
error_out('Could not resolve tenant_id for tenant %s' % tenant) error_out('Could not resolve tenant_id for tenant %s' % tenant)
manager.api.users.create(name=name, manager.api.users.create(name=name,
password=password, password=password,
email='juju@localhost', email='juju@localhost',
tenant_id=tenant_id) tenant_id=tenant_id)
log("Created new user '%s' tenant: %s" % (name, tenant_id)) log("Created new user '%s' tenant: %s" % (name, tenant_id),
return level=DEBUG)
log("A user named '%s' already exists" % name)
def create_role(name, user=None, tenant=None): def create_role(name, user=None, tenant=None):
@ -554,9 +571,9 @@ def create_role(name, user=None, tenant=None):
roles = [r._info for r in manager.api.roles.list()] roles = [r._info for r in manager.api.roles.list()]
if not roles or name not in [r['name'] for r in roles]: if not roles or name not in [r['name'] for r in roles]:
manager.api.roles.create(name=name) manager.api.roles.create(name=name)
log("Created new role '%s'" % name) log("Created new role '%s'" % name, level=DEBUG)
else: else:
log("A role named '%s' already exists" % name) log("A role named '%s' already exists" % name, level=DEBUG)
if not user and not tenant: if not user and not tenant:
return return
@ -590,10 +607,10 @@ def grant_role(user, role, tenant):
role=role_id, role=role_id,
tenant=tenant_id) tenant=tenant_id)
log("Granted user '%s' role '%s' on tenant '%s'" % log("Granted user '%s' role '%s' on tenant '%s'" %
(user, role, tenant)) (user, role, tenant), level=DEBUG)
else: else:
log("User '%s' already has role '%s' on tenant '%s'" % log("User '%s' already has role '%s' on tenant '%s'" %
(user, role, tenant)) (user, role, tenant), level=DEBUG)
def store_admin_passwd(passwd): def store_admin_passwd(passwd):
@ -663,10 +680,9 @@ def ensure_initial_admin(config):
'ldap' and config('ldap-readonly')): 'ldap' and config('ldap-readonly')):
passwd = get_admin_passwd() passwd = get_admin_passwd()
if passwd: if passwd:
create_user(config('admin-user'), passwd, tenant='admin') create_user_credentials(config('admin-user'), 'admin', passwd,
update_user_password(config('admin-user'), passwd) new_roles=[config('admin-role')])
create_role(config('admin-role'), config('admin-user'),
'admin')
create_service_entry("keystone", "identity", create_service_entry("keystone", "identity",
"Keystone Identity Service") "Keystone Identity Service")
@ -1260,6 +1276,50 @@ def relation_list(rid):
return result return result
def create_user_credentials(user, tenant, passwd, new_roles=None, grants=None):
"""Create user credentials.
Optionally adds role grants to user and/or creates new roles.
"""
log("Creating service credentials for '%s'" % user, level=DEBUG)
if user_exists(user):
log("User '%s' already exists - updating password" % (user),
level=DEBUG)
update_user_password(user, passwd)
else:
create_user(user, passwd, tenant)
if grants:
for role in grants:
grant_role(user, role, tenant)
else:
log("No role grants requested for user '%s'" % (user), level=DEBUG)
if new_roles:
# Allow the remote service to request creation of any additional roles.
# Currently used by Swift and Ceilometer.
for role in new_roles:
log("Creating requested role '%s'" % role, level=DEBUG)
create_role(role, user, tenant)
return passwd
def create_service_credentials(user, new_roles=None):
"""Create credentials for service with given username.
Services are given a user under config('service-tenant') and are given the
config('admin-role') role. Tenant is assumed to already exist,
"""
tenant = config('service-tenant')
if not tenant:
raise Exception("No service tenant provided in config")
return create_user_credentials(user, tenant, get_service_password(user),
new_roles=new_roles,
grants=[config('admin-role')])
def add_service_to_keystone(relation_id=None, remote_unit=None): def add_service_to_keystone(relation_id=None, remote_unit=None):
import manager import manager
manager = manager.KeystoneManager(endpoint=get_local_endpoint(), manager = manager.KeystoneManager(endpoint=get_local_endpoint(),
@ -1390,18 +1450,9 @@ def add_service_to_keystone(relation_id=None, remote_unit=None):
return return
token = get_admin_token() token = get_admin_token()
log("Creating service credentials for '%s'" % service_username) roles = get_requested_roles(settings)
service_password = create_service_credentials(service_username,
service_password = get_service_password(service_username) new_roles=roles)
create_user(service_username, service_password, config('service-tenant'))
grant_role(service_username, config('admin-role'),
config('service-tenant'))
# Allow the remote service to request creation of any additional roles.
# Currently used by Swift and Ceilometer.
for role in get_requested_roles(settings):
log("Creating requested role: %s" % role)
create_role(role, service_username, config('service-tenant'))
# As of https://review.openstack.org/#change,4675, all nodes hosting # As of https://review.openstack.org/#change,4675, all nodes hosting
# an endpoint(s) needs a service username and password assigned to # an endpoint(s) needs a service username and password assigned to

View File

@ -49,7 +49,8 @@ provider = keystone.token.providers.pki.Provider
provider = keystone.token.providers.pkiz.Provider provider = keystone.token.providers.pkiz.Provider
{% else -%} {% else -%}
provider = keystone.token.providers.uuid.Provider provider = keystone.token.providers.uuid.Provider
{% endif %} {% endif -%}
expiration = {{ token_expiration }}
{% include "parts/section-signing" %} {% include "parts/section-signing" %}

View File

@ -45,7 +45,16 @@ driver = keystone.catalog.backends.sql.Catalog
[token] [token]
driver = keystone.token.persistence.backends.sql.Token driver = keystone.token.persistence.backends.sql.Token
{% if token_provider == 'pki' -%}
provider = keystone.token.providers.pki.Provider
{% elif token_provider == 'pkiz' -%}
provider = keystone.token.providers.pkiz.Provider
{% else -%}
provider = keystone.token.providers.uuid.Provider provider = keystone.token.providers.uuid.Provider
{% endif -%}
expiration = {{ token_expiration }}
{% include "parts/section-signing" %}
[cache] [cache]

View File

@ -303,6 +303,63 @@ class TestKeystoneUtils(CharmTestCase):
adminurl='10.0.0.2', adminurl='10.0.0.2',
internalurl='192.168.1.2') internalurl='192.168.1.2')
@patch.object(utils, 'user_exists')
@patch.object(utils, 'grant_role')
@patch.object(utils, 'create_role')
@patch.object(utils, 'create_user')
def test_create_user_credentials_no_roles(self, mock_create_user,
mock_create_role,
mock_grant_role,
mock_user_exists):
mock_user_exists.return_value = False
utils.create_user_credentials('userA', 'tenantA', 'passA')
mock_create_user.assert_has_calls([call('userA', 'passA', 'tenantA')])
mock_create_role.assert_has_calls([])
mock_grant_role.assert_has_calls([])
@patch.object(utils, 'user_exists')
@patch.object(utils, 'grant_role')
@patch.object(utils, 'create_role')
@patch.object(utils, 'create_user')
def test_create_user_credentials(self, mock_create_user, mock_create_role,
mock_grant_role, mock_user_exists):
mock_user_exists.return_value = False
utils.create_user_credentials('userA', 'tenantA', 'passA',
grants=['roleA'], new_roles=['roleB'])
mock_create_user.assert_has_calls([call('userA', 'passA', 'tenantA')])
mock_create_role.assert_has_calls([call('roleB', 'userA', 'tenantA')])
mock_grant_role.assert_has_calls([call('userA', 'roleA', 'tenantA')])
@patch.object(utils, 'update_user_password')
@patch.object(utils, 'user_exists')
@patch.object(utils, 'grant_role')
@patch.object(utils, 'create_role')
@patch.object(utils, 'create_user')
def test_create_user_credentials_user_exists(self, mock_create_user,
mock_create_role,
mock_grant_role,
mock_user_exists,
mock_update_user_password):
mock_user_exists.return_value = True
utils.create_user_credentials('userA', 'tenantA', 'passA',
grants=['roleA'], new_roles=['roleB'])
mock_create_user.assert_has_calls([])
mock_create_role.assert_has_calls([call('roleB', 'userA', 'tenantA')])
mock_grant_role.assert_has_calls([call('userA', 'roleA', 'tenantA')])
mock_update_user_password.assert_has_calls([call('userA', 'passA')])
@patch.object(utils, 'get_service_password')
@patch.object(utils, 'create_user_credentials')
def test_create_service_credentials(self, mock_create_user_credentials,
mock_get_service_password):
mock_get_service_password.return_value = 'passA'
cfg = {'service-tenant': 'tenantA', 'admin-role': 'Admin'}
self.config.side_effect = lambda key: cfg.get(key, None)
calls = [call('serviceA', 'tenantA', 'passA', grants=['Admin'],
new_roles=None)]
utils.create_service_credentials('serviceA')
mock_create_user_credentials.assert_has_calls(calls)
def test_ensure_valid_service_incorrect(self): def test_ensure_valid_service_incorrect(self):
utils.ensure_valid_service('fakeservice') utils.ensure_valid_service('fakeservice')
self.log.assert_called_with("Invalid service requested: 'fakeservice'") self.log.assert_called_with("Invalid service requested: 'fakeservice'")