Create service credentials in SERVICE_DOMAIN
Cleanup code that references users, projects or domains without necessary scoping or filtering throughout the charm. Add logging of domain name in contexts where this is relevant. Tighten rule:service_role to require role:service and token scoped to project config('service-tenant') created in SERVICE_DOMAIN. This ensures that if you have a deployment with end-user access to assign roles within their own domains they will not gain privileged access simply by assigning the service role to one of their own users. Allow users authorized by rule:service_role to perform identity:list_projects. This is required to allow Ceilometer to operate without Admin privileges. Services are given a user in project config('service-tenant') in SERVICE_DOMAIN for v3 authentication / authorization. As of Mitaka Keystone v3 policy the 'service' role is sufficient for services to validate tokens. Services are also given a user in project config('service-tenant') in DEFAULT_DOMAIN to support services still configured with v2.0 authentication / authorization. This will allow us to transition from v2.0 based authentication / authorization and existing services and charms will continue to operate as before. This will also allow the end-user to roll their deployment up to api_version 3 and back to api_version 2 as needed. Services and charms that has made the transition to fully use the v3 API for authentication and authorization will gain full access to domains and projects across the deployment. The first charm to make use of this is charm-ceilometer. Closes-Bug: 1636098 Change-Id: If1518029c43476a5e14bf94596197eabe663499c
This commit is contained in:

committed by
Frode Nordahl

parent
10e3d84eff
commit
5de1770931
@@ -92,6 +92,7 @@ from charmhelpers.core.hookenv import (
|
|||||||
charm_dir,
|
charm_dir,
|
||||||
config,
|
config,
|
||||||
is_relation_made,
|
is_relation_made,
|
||||||
|
leader_set,
|
||||||
log,
|
log,
|
||||||
local_unit,
|
local_unit,
|
||||||
relation_get,
|
relation_get,
|
||||||
@@ -204,6 +205,7 @@ SSL_SYNC_SEMAPHORE = threading.Semaphore()
|
|||||||
SSL_DIRS = [SSL_DIR, APACHE_SSL_DIR, CA_CERT_PATH]
|
SSL_DIRS = [SSL_DIR, APACHE_SSL_DIR, CA_CERT_PATH]
|
||||||
ADMIN_DOMAIN = 'admin_domain'
|
ADMIN_DOMAIN = 'admin_domain'
|
||||||
DEFAULT_DOMAIN = 'default'
|
DEFAULT_DOMAIN = 'default'
|
||||||
|
SERVICE_DOMAIN = 'service_domain'
|
||||||
POLICY_JSON = '/etc/keystone/policy.json'
|
POLICY_JSON = '/etc/keystone/policy.json'
|
||||||
TOKEN_FLUSH_CRON_FILE = '/etc/cron.d/keystone-token-flush'
|
TOKEN_FLUSH_CRON_FILE = '/etc/cron.d/keystone-token-flush'
|
||||||
WSGI_KEYSTONE_CONF = '/etc/apache2/sites-enabled/wsgi-keystone.conf'
|
WSGI_KEYSTONE_CONF = '/etc/apache2/sites-enabled/wsgi-keystone.conf'
|
||||||
@@ -737,14 +739,16 @@ def create_endpoint_template_v3(manager, region, service, publicurl, adminurl,
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_tenant(name):
|
def create_tenant(name, domain):
|
||||||
"""Creates a tenant if it does not already exist"""
|
"""Creates a tenant if it does not already exist"""
|
||||||
manager = get_manager()
|
manager = get_manager()
|
||||||
tenant = manager.resolve_tenant_id(name)
|
tenant = manager.resolve_tenant_id(name, domain=domain)
|
||||||
if not tenant:
|
if not tenant:
|
||||||
manager.create_tenant(tenant_name=name,
|
manager.create_tenant(tenant_name=name,
|
||||||
|
domain=domain,
|
||||||
description='Created by Juju')
|
description='Created by Juju')
|
||||||
log("Created new tenant: %s" % name, level=DEBUG)
|
log("Created new tenant '%s' in domain '%s'" % (name, domain),
|
||||||
|
level=DEBUG)
|
||||||
return
|
return
|
||||||
|
|
||||||
log("Tenant '%s' already exists." % name, level=DEBUG)
|
log("Tenant '%s' already exists." % name, level=DEBUG)
|
||||||
@@ -789,14 +793,16 @@ def create_user(name, password, tenant=None, domain=None):
|
|||||||
"""Creates a user if it doesn't already exist, as a member of tenant"""
|
"""Creates a user if it doesn't already exist, as a member of tenant"""
|
||||||
manager = get_manager()
|
manager = get_manager()
|
||||||
if user_exists(name, domain=domain):
|
if user_exists(name, domain=domain):
|
||||||
log("A user named '%s' already exists" % name, level=DEBUG)
|
log("A user named '%s' already exists in domain '%s'" % (name, domain),
|
||||||
|
level=DEBUG)
|
||||||
return
|
return
|
||||||
|
|
||||||
tenant_id = None
|
tenant_id = None
|
||||||
if tenant:
|
if tenant:
|
||||||
tenant_id = manager.resolve_tenant_id(tenant)
|
tenant_id = manager.resolve_tenant_id(tenant, domain=domain)
|
||||||
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' in domain "
|
||||||
|
"'%s'" % (tenant, domain))
|
||||||
|
|
||||||
domain_id = None
|
domain_id = None
|
||||||
if domain:
|
if domain:
|
||||||
@@ -810,8 +816,8 @@ def create_user(name, password, tenant=None, domain=None):
|
|||||||
email='juju@localhost',
|
email='juju@localhost',
|
||||||
tenant_id=tenant_id,
|
tenant_id=tenant_id,
|
||||||
domain_id=domain_id)
|
domain_id=domain_id)
|
||||||
log("Created new user '%s' tenant: %s" % (name, tenant_id),
|
log("Created new user '%s' tenant: '%s' domain: '%s'" % (name, tenant_id,
|
||||||
level=DEBUG)
|
domain_id), level=DEBUG)
|
||||||
|
|
||||||
|
|
||||||
def get_manager(api_version=None):
|
def get_manager(api_version=None):
|
||||||
@@ -834,33 +840,41 @@ def create_role(name, user=None, tenant=None, domain=None):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# NOTE(adam_g): Keystone client requires id's for add_user_role, not names
|
# NOTE(adam_g): Keystone client requires id's for add_user_role, not names
|
||||||
user_id = manager.resolve_user_id(user)
|
user_id = manager.resolve_user_id(user, user_domain=domain)
|
||||||
role_id = manager.resolve_role_id(name)
|
role_id = manager.resolve_role_id(name)
|
||||||
|
|
||||||
if None in [user_id, role_id]:
|
if None in [user_id, role_id]:
|
||||||
error_out("Could not resolve [%s, %s]" %
|
error_out("Could not resolve [%s, %s] user_domain='%s'" %
|
||||||
(user_id, role_id))
|
(user_id, role_id, domain))
|
||||||
|
|
||||||
grant_role(user, name, tenant, domain)
|
# default to grant role to project
|
||||||
|
grant_role(user, name, tenant=tenant, user_domain=domain,
|
||||||
|
project_domain=domain)
|
||||||
|
|
||||||
|
|
||||||
def grant_role(user, role, tenant=None, domain=None, user_domain=None):
|
def grant_role(user, role, tenant=None, domain=None, user_domain=None,
|
||||||
|
project_domain=None):
|
||||||
"""Grant user and tenant a specific role"""
|
"""Grant user and tenant a specific role"""
|
||||||
manager = get_manager()
|
manager = get_manager()
|
||||||
log("Granting user '%s' role '%s' on tenant '%s'" %
|
if domain:
|
||||||
(user, role, tenant))
|
log("Granting user '%s' role '%s' in domain '%s'" %
|
||||||
|
(user, role, domain))
|
||||||
|
else:
|
||||||
|
log("Granting user '%s' role '%s' on tenant '%s' in domain '%s'" %
|
||||||
|
(user, role, tenant, project_domain))
|
||||||
|
|
||||||
user_id = manager.resolve_user_id(user, user_domain=user_domain)
|
user_id = manager.resolve_user_id(user, user_domain=user_domain)
|
||||||
role_id = manager.resolve_role_id(role)
|
role_id = manager.resolve_role_id(role)
|
||||||
if None in [user_id, role_id]:
|
if None in [user_id, role_id]:
|
||||||
error_out("Could not resolve [%s, %s]" %
|
error_out("Could not resolve [%s, %s] user_domain='%s'" %
|
||||||
(user_id, role_id))
|
(user_id, role_id, user_domain))
|
||||||
|
|
||||||
tenant_id = None
|
tenant_id = None
|
||||||
if tenant:
|
if tenant:
|
||||||
tenant_id = manager.resolve_tenant_id(tenant)
|
tenant_id = manager.resolve_tenant_id(tenant, domain=project_domain)
|
||||||
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' in domain "
|
||||||
|
"'%s'" % (tenant, domain))
|
||||||
|
|
||||||
domain_id = None
|
domain_id = None
|
||||||
if domain:
|
if domain:
|
||||||
@@ -875,11 +889,19 @@ def grant_role(user, role, tenant=None, domain=None, user_domain=None):
|
|||||||
role=role_id,
|
role=role_id,
|
||||||
tenant=tenant_id,
|
tenant=tenant_id,
|
||||||
domain=domain_id)
|
domain=domain_id)
|
||||||
log("Granted user '%s' role '%s' on tenant '%s'" %
|
if domain_id is None:
|
||||||
(user, role, tenant), level=DEBUG)
|
log("Granted user '%s' role '%s' on tenant '%s' in domain '%s'" %
|
||||||
|
(user, role, tenant, project_domain), level=DEBUG)
|
||||||
|
else:
|
||||||
|
log("Granted user '%s' role '%s' in domain '%s'" %
|
||||||
|
(user, role, domain), level=DEBUG)
|
||||||
else:
|
else:
|
||||||
log("User '%s' already has role '%s' on tenant '%s'" %
|
if domain_id is None:
|
||||||
(user, role, tenant), level=DEBUG)
|
log("User '%s' already has role '%s' on tenant '%s' in domain '%s'"
|
||||||
|
% (user, role, tenant, project_domain), level=DEBUG)
|
||||||
|
else:
|
||||||
|
log("User '%s' already has role '%s' in domain '%s'"
|
||||||
|
% (user, role, domain), level=DEBUG)
|
||||||
|
|
||||||
|
|
||||||
def store_data(backing_file, data):
|
def store_data(backing_file, data):
|
||||||
@@ -962,12 +984,20 @@ def ensure_initial_admin(config):
|
|||||||
changes?
|
changes?
|
||||||
"""
|
"""
|
||||||
if get_api_version() > 2:
|
if get_api_version() > 2:
|
||||||
|
manager = get_manager()
|
||||||
default_domain_id = create_or_show_domain(DEFAULT_DOMAIN)
|
default_domain_id = create_or_show_domain(DEFAULT_DOMAIN)
|
||||||
store_default_domain_id(default_domain_id)
|
store_default_domain_id(default_domain_id)
|
||||||
admin_domain_id = create_or_show_domain(ADMIN_DOMAIN)
|
admin_domain_id = create_or_show_domain(ADMIN_DOMAIN)
|
||||||
store_admin_domain_id(admin_domain_id)
|
store_admin_domain_id(admin_domain_id)
|
||||||
create_tenant("admin")
|
create_or_show_domain(SERVICE_DOMAIN)
|
||||||
create_tenant(config("service-tenant"))
|
create_tenant("admin", ADMIN_DOMAIN)
|
||||||
|
create_tenant(config("service-tenant"), SERVICE_DOMAIN)
|
||||||
|
leader_set({'service_tenant_id': manager.resolve_tenant_id(
|
||||||
|
config("service-tenant"),
|
||||||
|
domain=SERVICE_DOMAIN)})
|
||||||
|
create_role('service')
|
||||||
|
create_tenant("admin", DEFAULT_DOMAIN)
|
||||||
|
create_tenant(config("service-tenant"), DEFAULT_DOMAIN)
|
||||||
# User is managed by ldap backend when using ldap identity
|
# User is managed by ldap backend when using ldap identity
|
||||||
if not (config('identity-backend') ==
|
if not (config('identity-backend') ==
|
||||||
'ldap' and config('ldap-readonly')):
|
'ldap' and config('ldap-readonly')):
|
||||||
@@ -976,10 +1006,20 @@ def ensure_initial_admin(config):
|
|||||||
if get_api_version() > 2:
|
if get_api_version() > 2:
|
||||||
create_user_credentials(config('admin-user'), passwd,
|
create_user_credentials(config('admin-user'), passwd,
|
||||||
domain=ADMIN_DOMAIN)
|
domain=ADMIN_DOMAIN)
|
||||||
create_role(config('admin-role'), config('admin-user'),
|
create_role('Member')
|
||||||
domain=ADMIN_DOMAIN)
|
# Grant 'Member' role to user ADMIN_DOMAIN/admin-user in
|
||||||
|
# project ADMIN_DOMAIN/'admin'
|
||||||
|
# ADMIN_DOMAIN
|
||||||
|
grant_role(config('admin-user'), 'Member', tenant='admin',
|
||||||
|
user_domain=ADMIN_DOMAIN,
|
||||||
|
project_domain=ADMIN_DOMAIN)
|
||||||
|
create_role(config('admin-role'))
|
||||||
|
# Grant admin-role to user ADMIN_DOMAIN/admin-user in
|
||||||
|
# project ADMIN_DOMAIN/admin
|
||||||
grant_role(config('admin-user'), config('admin-role'),
|
grant_role(config('admin-user'), config('admin-role'),
|
||||||
tenant='admin', user_domain=ADMIN_DOMAIN)
|
tenant='admin', user_domain=ADMIN_DOMAIN,
|
||||||
|
project_domain=ADMIN_DOMAIN)
|
||||||
|
# Grant domain level admin-role to ADMIN_DOMAIN/admin-user
|
||||||
grant_role(config('admin-user'), config('admin-role'),
|
grant_role(config('admin-user'), config('admin-role'),
|
||||||
domain=ADMIN_DOMAIN, user_domain=ADMIN_DOMAIN)
|
domain=ADMIN_DOMAIN, user_domain=ADMIN_DOMAIN)
|
||||||
else:
|
else:
|
||||||
@@ -1623,11 +1663,13 @@ def create_user_credentials(user, passwd, tenant=None, new_roles=None,
|
|||||||
level=DEBUG)
|
level=DEBUG)
|
||||||
update_user_password(user, passwd)
|
update_user_password(user, passwd)
|
||||||
else:
|
else:
|
||||||
create_user(user, passwd, tenant, domain)
|
create_user(user, passwd, tenant=tenant, domain=domain)
|
||||||
|
|
||||||
if grants:
|
if grants:
|
||||||
for role in grants:
|
for role in grants:
|
||||||
grant_role(user, role, tenant, domain)
|
# grant role on project
|
||||||
|
grant_role(user, role, tenant=tenant, user_domain=domain,
|
||||||
|
project_domain=domain)
|
||||||
else:
|
else:
|
||||||
log("No role grants requested for user '%s'" % (user), level=DEBUG)
|
log("No role grants requested for user '%s'" % (user), level=DEBUG)
|
||||||
|
|
||||||
@@ -1636,7 +1678,7 @@ def create_user_credentials(user, passwd, tenant=None, new_roles=None,
|
|||||||
# Currently used by Swift and Ceilometer.
|
# Currently used by Swift and Ceilometer.
|
||||||
for role in new_roles:
|
for role in new_roles:
|
||||||
log("Creating requested role '%s'" % role, level=DEBUG)
|
log("Creating requested role '%s'" % role, level=DEBUG)
|
||||||
create_role(role, user, tenant, domain)
|
create_role(role, user=user, tenant=tenant, domain=domain)
|
||||||
|
|
||||||
return passwd
|
return passwd
|
||||||
|
|
||||||
@@ -1644,21 +1686,36 @@ def create_user_credentials(user, passwd, tenant=None, new_roles=None,
|
|||||||
def create_service_credentials(user, new_roles=None):
|
def create_service_credentials(user, new_roles=None):
|
||||||
"""Create credentials for service with given username.
|
"""Create credentials for service with given username.
|
||||||
|
|
||||||
Services are given a user under config('service-tenant') and are given the
|
For Keystone v2.0 API compability services are given a user under
|
||||||
config('admin-role') role. Tenant is assumed to already exist,
|
config('service-tenant') in DEFAULT_DOMAIN and are given the
|
||||||
|
config('admin-role') role. Tenant is assumed to already exist.
|
||||||
|
|
||||||
|
For Keysteone v3 API compability services are given a user in project
|
||||||
|
config('service-tenant') in SERVICE_DOMAIN and are given the 'service'
|
||||||
|
role.
|
||||||
|
|
||||||
|
As of Mitaka Keystone v3 policy the 'service' role is sufficient for
|
||||||
|
services to validate tokens. Project is assumed to already exist.
|
||||||
"""
|
"""
|
||||||
tenant = config('service-tenant')
|
tenant = config('service-tenant')
|
||||||
if not tenant:
|
if not tenant:
|
||||||
raise Exception("No service tenant provided in config")
|
raise Exception("No service tenant provided in config")
|
||||||
|
|
||||||
if get_api_version() == 2:
|
domain = None
|
||||||
domain = None
|
if get_api_version() > 2:
|
||||||
else:
|
|
||||||
domain = DEFAULT_DOMAIN
|
domain = DEFAULT_DOMAIN
|
||||||
return create_user_credentials(user, get_service_password(user),
|
passwd = create_user_credentials(user, get_service_password(user),
|
||||||
tenant=tenant, new_roles=new_roles,
|
tenant=tenant, new_roles=new_roles,
|
||||||
grants=[config('admin-role')],
|
grants=[config('admin-role')],
|
||||||
domain=domain)
|
domain=domain)
|
||||||
|
if get_api_version() > 2:
|
||||||
|
# v3 policy allows services to validate tokens when granted the
|
||||||
|
# 'service' role.
|
||||||
|
domain = SERVICE_DOMAIN
|
||||||
|
passwd = create_user_credentials(user, passwd,
|
||||||
|
tenant=tenant, new_roles=new_roles,
|
||||||
|
grants=['service'], domain=domain)
|
||||||
|
return passwd
|
||||||
|
|
||||||
|
|
||||||
def add_service_to_keystone(relation_id=None, remote_unit=None):
|
def add_service_to_keystone(relation_id=None, remote_unit=None):
|
||||||
@@ -1783,16 +1840,12 @@ def add_service_to_keystone(relation_id=None, remote_unit=None):
|
|||||||
roles = get_requested_roles(settings)
|
roles = get_requested_roles(settings)
|
||||||
service_password = create_service_credentials(service_username,
|
service_password = create_service_credentials(service_username,
|
||||||
new_roles=roles)
|
new_roles=roles)
|
||||||
|
service_domain = None
|
||||||
# As of https://review.openstack.org/#change,4675, all nodes hosting
|
if get_api_version() > 2:
|
||||||
# an endpoint(s) needs a service username and password assigned to
|
service_domain = SERVICE_DOMAIN
|
||||||
# the service tenant and granted admin role.
|
|
||||||
# note: config('service-tenant') is created in utils.ensure_initial_admin()
|
|
||||||
# we return a token, information about our API endpoints, and the generated
|
|
||||||
# service credentials
|
|
||||||
service_tenant = config('service-tenant')
|
service_tenant = config('service-tenant')
|
||||||
domain_name = 'Default' if manager.api_version == 3 else None
|
service_tenant_id = manager.resolve_tenant_id(service_tenant,
|
||||||
grant_role(service_username, 'Admin', service_tenant, domain_name)
|
domain=service_domain)
|
||||||
|
|
||||||
# NOTE(dosaboy): we use __null__ to represent settings that are to be
|
# NOTE(dosaboy): we use __null__ to represent settings that are to be
|
||||||
# routed to relations via the cluster relation and set to None.
|
# routed to relations via the cluster relation and set to None.
|
||||||
@@ -1804,8 +1857,9 @@ def add_service_to_keystone(relation_id=None, remote_unit=None):
|
|||||||
"auth_port": config("admin-port"),
|
"auth_port": config("admin-port"),
|
||||||
"service_username": service_username,
|
"service_username": service_username,
|
||||||
"service_password": service_password,
|
"service_password": service_password,
|
||||||
|
"service_domain": service_domain,
|
||||||
"service_tenant": service_tenant,
|
"service_tenant": service_tenant,
|
||||||
"service_tenant_id": manager.resolve_tenant_id(service_tenant),
|
"service_tenant_id": service_tenant_id,
|
||||||
"https_keystone": '__null__',
|
"https_keystone": '__null__',
|
||||||
"ssl_cert": '__null__',
|
"ssl_cert": '__null__',
|
||||||
"ssl_key": '__null__',
|
"ssl_key": '__null__',
|
||||||
@@ -1860,16 +1914,17 @@ def add_credentials_to_keystone(relation_id=None, remote_unit=None):
|
|||||||
|
|
||||||
if get_api_version() == 2:
|
if get_api_version() == 2:
|
||||||
domain = None
|
domain = None
|
||||||
|
grants = [config('admin-role')]
|
||||||
else:
|
else:
|
||||||
domain = settings.get('domain') or DEFAULT_DOMAIN
|
domain = settings.get('domain') or SERVICE_DOMAIN
|
||||||
|
grants = ['service']
|
||||||
|
|
||||||
# Use passed project or the service project
|
# Use passed project or the service project
|
||||||
credentials_project = settings.get('project') or config('service-tenant')
|
credentials_project = settings.get('project') or config('service-tenant')
|
||||||
create_tenant(credentials_project)
|
create_tenant(credentials_project, domain)
|
||||||
|
|
||||||
# Use passed grants or default to granting the Admin role
|
# Use passed grants or default grants
|
||||||
credentials_grants = (get_requested_grants(settings) or
|
credentials_grants = (get_requested_grants(settings) or grants)
|
||||||
[config('admin-role')])
|
|
||||||
|
|
||||||
# Create the user
|
# Create the user
|
||||||
credentials_password = create_user_credentials(
|
credentials_password = create_user_credentials(
|
||||||
@@ -1891,7 +1946,7 @@ def add_credentials_to_keystone(relation_id=None, remote_unit=None):
|
|||||||
"credentials_password": credentials_password,
|
"credentials_password": credentials_password,
|
||||||
"credentials_project": credentials_project,
|
"credentials_project": credentials_project,
|
||||||
"credentials_project_id":
|
"credentials_project_id":
|
||||||
manager.resolve_tenant_id(credentials_project),
|
manager.resolve_tenant_id(credentials_project, domain=domain),
|
||||||
"auth_protocol": protocol,
|
"auth_protocol": protocol,
|
||||||
"credentials_protocol": protocol,
|
"credentials_protocol": protocol,
|
||||||
"api_version": get_api_version(),
|
"api_version": get_api_version(),
|
||||||
|
@@ -91,13 +91,6 @@ def get_keystone_manager(endpoint, token, api_version=None):
|
|||||||
|
|
||||||
class KeystoneManager(object):
|
class KeystoneManager(object):
|
||||||
|
|
||||||
def resolve_tenant_id(self, name):
|
|
||||||
"""Find the tenant_id of a given tenant"""
|
|
||||||
tenants = [t._info for t in self.api.tenants.list()]
|
|
||||||
for t in tenants:
|
|
||||||
if name.lower() == t['name'].lower():
|
|
||||||
return t['id']
|
|
||||||
|
|
||||||
def resolve_domain_id(self, name):
|
def resolve_domain_id(self, name):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -150,6 +143,13 @@ class KeystoneManager2(KeystoneManager):
|
|||||||
def tenants_list(self):
|
def tenants_list(self):
|
||||||
return self.api.tenants.list()
|
return self.api.tenants.list()
|
||||||
|
|
||||||
|
def resolve_tenant_id(self, name, domain=None):
|
||||||
|
"""Find the tenant_id of a given tenant"""
|
||||||
|
tenants = [t._info for t in self.api.tenants.list()]
|
||||||
|
for t in tenants:
|
||||||
|
if name.lower() == t['name'].lower():
|
||||||
|
return t['id']
|
||||||
|
|
||||||
def create_tenant(self, tenant_name, description, domain='default'):
|
def create_tenant(self, tenant_name, description, domain='default'):
|
||||||
self.api.tenants.create(tenant_name=tenant_name,
|
self.api.tenants.create(tenant_name=tenant_name,
|
||||||
description=description)
|
description=description)
|
||||||
@@ -182,11 +182,14 @@ class KeystoneManager3(KeystoneManager):
|
|||||||
keystone_session_v3 = session.Session(auth=keystone_auth_v3)
|
keystone_session_v3 = session.Session(auth=keystone_auth_v3)
|
||||||
self.api = keystoneclient_v3.Client(session=keystone_session_v3)
|
self.api = keystoneclient_v3.Client(session=keystone_session_v3)
|
||||||
|
|
||||||
def resolve_tenant_id(self, name):
|
def resolve_tenant_id(self, name, domain=None):
|
||||||
"""Find the tenant_id of a given tenant"""
|
"""Find the tenant_id of a given tenant"""
|
||||||
|
if domain:
|
||||||
|
domain_id = self.resolve_domain_id(domain)
|
||||||
tenants = [t._info for t in self.api.projects.list()]
|
tenants = [t._info for t in self.api.projects.list()]
|
||||||
for t in tenants:
|
for t in tenants:
|
||||||
if name.lower() == t['name'].lower():
|
if name.lower() == t['name'].lower() and \
|
||||||
|
(domain is None or t['domain_id'] == domain_id):
|
||||||
return t['id']
|
return t['id']
|
||||||
|
|
||||||
def resolve_domain_id(self, name):
|
def resolve_domain_id(self, name):
|
||||||
|
@@ -261,15 +261,15 @@ class TestKeystoneUtils(CharmTestCase):
|
|||||||
def test_add_service_to_keystone_no_clustered_no_https_complete_values(
|
def test_add_service_to_keystone_no_clustered_no_https_complete_values(
|
||||||
self, KeystoneManager, add_endpoint, ensure_valid_service,
|
self, KeystoneManager, add_endpoint, ensure_valid_service,
|
||||||
_resolve_address, create_user, get_admin_domain_id,
|
_resolve_address, create_user, get_admin_domain_id,
|
||||||
get_api_version):
|
get_api_version, test_api_version=2):
|
||||||
get_admin_domain_id.return_value = None
|
get_admin_domain_id.return_value = None
|
||||||
get_api_version.return_value = 2
|
get_api_version.return_value = test_api_version
|
||||||
relation_id = 'identity-service:0'
|
relation_id = 'identity-service:0'
|
||||||
remote_unit = 'unit/0'
|
remote_unit = 'unit/0'
|
||||||
self.get_admin_token.return_value = 'token'
|
self.get_admin_token.return_value = 'token'
|
||||||
self.get_service_password.return_value = 'password'
|
self.get_service_password.return_value = 'password'
|
||||||
self.test_config.set('service-tenant', 'tenant')
|
self.test_config.set('service-tenant', 'tenant')
|
||||||
self.test_config.set('admin-role', 'admin')
|
self.test_config.set('admin-role', 'Admin')
|
||||||
self.get_requested_roles.return_value = ['role1', ]
|
self.get_requested_roles.return_value = ['role1', ]
|
||||||
_resolve_address.return_value = '10.0.0.3'
|
_resolve_address.return_value = '10.0.0.3'
|
||||||
self.test_config.set('admin-port', 80)
|
self.test_config.set('admin-port', 80)
|
||||||
@@ -279,6 +279,12 @@ class TestKeystoneUtils(CharmTestCase):
|
|||||||
self.get_local_endpoint.return_value = 'http://localhost:80/v2.0/'
|
self.get_local_endpoint.return_value = 'http://localhost:80/v2.0/'
|
||||||
self.relation_ids.return_value = ['cluster/0']
|
self.relation_ids.return_value = ['cluster/0']
|
||||||
|
|
||||||
|
service_domain = None
|
||||||
|
service_role = 'Admin'
|
||||||
|
if test_api_version > 2:
|
||||||
|
service_domain = 'service_domain'
|
||||||
|
service_role = 'service'
|
||||||
|
|
||||||
mock_keystone = MagicMock()
|
mock_keystone = MagicMock()
|
||||||
mock_keystone.resolve_tenant_id.return_value = 'tenant_id'
|
mock_keystone.resolve_tenant_id.return_value = 'tenant_id'
|
||||||
KeystoneManager.return_value = mock_keystone
|
KeystoneManager.return_value = mock_keystone
|
||||||
@@ -299,23 +305,31 @@ class TestKeystoneUtils(CharmTestCase):
|
|||||||
internalurl='192.168.1.2')
|
internalurl='192.168.1.2')
|
||||||
self.assertTrue(self.get_admin_token.called)
|
self.assertTrue(self.get_admin_token.called)
|
||||||
self.get_service_password.assert_called_with('keystone')
|
self.get_service_password.assert_called_with('keystone')
|
||||||
create_user.assert_called_with('keystone', 'password', 'tenant', None)
|
create_user.assert_called_with('keystone', 'password',
|
||||||
self.grant_role.assert_called_with('keystone', 'Admin', 'tenant',
|
domain=service_domain,
|
||||||
None)
|
tenant='tenant')
|
||||||
self.create_role.assert_called_with('role1', 'keystone', 'tenant',
|
self.grant_role.assert_called_with('keystone', service_role,
|
||||||
None)
|
project_domain=service_domain,
|
||||||
|
tenant='tenant',
|
||||||
|
user_domain=service_domain)
|
||||||
|
self.create_role.assert_called_with('role1', user='keystone',
|
||||||
|
tenant='tenant',
|
||||||
|
domain=service_domain)
|
||||||
|
|
||||||
relation_data = {'admin_domain_id': None, 'auth_host': '10.0.0.3',
|
relation_data = {'admin_domain_id': None,
|
||||||
|
'auth_host': '10.0.0.3',
|
||||||
'service_host': '10.0.0.3', 'admin_token': 'token',
|
'service_host': '10.0.0.3', 'admin_token': 'token',
|
||||||
'service_port': 81, 'auth_port': 80,
|
'service_port': 81, 'auth_port': 80,
|
||||||
'service_username': 'keystone',
|
'service_username': 'keystone',
|
||||||
'service_password': 'password',
|
'service_password': 'password',
|
||||||
|
'service_domain': service_domain,
|
||||||
'service_tenant': 'tenant',
|
'service_tenant': 'tenant',
|
||||||
'https_keystone': '__null__',
|
'https_keystone': '__null__',
|
||||||
'ssl_cert': '__null__', 'ssl_key': '__null__',
|
'ssl_cert': '__null__', 'ssl_key': '__null__',
|
||||||
'ca_cert': '__null__',
|
'ca_cert': '__null__',
|
||||||
'auth_protocol': 'http', 'service_protocol': 'http',
|
'auth_protocol': 'http', 'service_protocol': 'http',
|
||||||
'service_tenant_id': 'tenant_id', 'api_version': 2}
|
'service_tenant_id': 'tenant_id',
|
||||||
|
'api_version': test_api_version}
|
||||||
|
|
||||||
filtered = {}
|
filtered = {}
|
||||||
for k, v in relation_data.iteritems():
|
for k, v in relation_data.iteritems():
|
||||||
@@ -330,6 +344,12 @@ class TestKeystoneUtils(CharmTestCase):
|
|||||||
self.relation_set.assert_called_with(relation_id=relation_id,
|
self.relation_set.assert_called_with(relation_id=relation_id,
|
||||||
**filtered)
|
**filtered)
|
||||||
|
|
||||||
|
def test_add_service_to_keystone_no_clustered_no_https_complete_values_v3(
|
||||||
|
self):
|
||||||
|
return self.\
|
||||||
|
test_add_service_to_keystone_no_clustered_no_https_complete_values(
|
||||||
|
test_api_version=3)
|
||||||
|
|
||||||
@patch('charmhelpers.contrib.openstack.ip.config')
|
@patch('charmhelpers.contrib.openstack.ip.config')
|
||||||
@patch.object(utils, 'ensure_valid_service')
|
@patch.object(utils, 'ensure_valid_service')
|
||||||
@patch.object(utils, 'add_endpoint')
|
@patch.object(utils, 'add_endpoint')
|
||||||
@@ -367,8 +387,9 @@ class TestKeystoneUtils(CharmTestCase):
|
|||||||
mock_user_exists):
|
mock_user_exists):
|
||||||
mock_user_exists.return_value = False
|
mock_user_exists.return_value = False
|
||||||
utils.create_user_credentials('userA', 'passA', tenant='tenantA')
|
utils.create_user_credentials('userA', 'passA', tenant='tenantA')
|
||||||
mock_create_user.assert_has_calls([call('userA', 'passA', 'tenantA',
|
mock_create_user.assert_has_calls([call('userA', 'passA',
|
||||||
None)])
|
domain=None,
|
||||||
|
tenant='tenantA')])
|
||||||
mock_create_role.assert_has_calls([])
|
mock_create_role.assert_has_calls([])
|
||||||
mock_grant_role.assert_has_calls([])
|
mock_grant_role.assert_has_calls([])
|
||||||
|
|
||||||
@@ -381,12 +402,16 @@ class TestKeystoneUtils(CharmTestCase):
|
|||||||
mock_user_exists.return_value = False
|
mock_user_exists.return_value = False
|
||||||
utils.create_user_credentials('userA', 'passA', tenant='tenantA',
|
utils.create_user_credentials('userA', 'passA', tenant='tenantA',
|
||||||
grants=['roleA'], new_roles=['roleB'])
|
grants=['roleA'], new_roles=['roleB'])
|
||||||
mock_create_user.assert_has_calls([call('userA', 'passA', 'tenantA',
|
mock_create_user.assert_has_calls([call('userA', 'passA',
|
||||||
None)])
|
tenant='tenantA',
|
||||||
mock_create_role.assert_has_calls([call('roleB', 'userA', 'tenantA',
|
domain=None)])
|
||||||
None)])
|
mock_create_role.assert_has_calls([call('roleB', user='userA',
|
||||||
mock_grant_role.assert_has_calls([call('userA', 'roleA', 'tenantA',
|
tenant='tenantA',
|
||||||
None)])
|
domain=None)])
|
||||||
|
mock_grant_role.assert_has_calls([call('userA', 'roleA',
|
||||||
|
tenant='tenantA',
|
||||||
|
user_domain=None,
|
||||||
|
project_domain=None)])
|
||||||
|
|
||||||
@patch.object(utils, 'update_user_password')
|
@patch.object(utils, 'update_user_password')
|
||||||
@patch.object(utils, 'user_exists')
|
@patch.object(utils, 'user_exists')
|
||||||
@@ -402,10 +427,13 @@ class TestKeystoneUtils(CharmTestCase):
|
|||||||
utils.create_user_credentials('userA', 'passA', tenant='tenantA',
|
utils.create_user_credentials('userA', 'passA', tenant='tenantA',
|
||||||
grants=['roleA'], new_roles=['roleB'])
|
grants=['roleA'], new_roles=['roleB'])
|
||||||
mock_create_user.assert_has_calls([])
|
mock_create_user.assert_has_calls([])
|
||||||
mock_create_role.assert_has_calls([call('roleB', 'userA', 'tenantA',
|
mock_create_role.assert_has_calls([call('roleB', user='userA',
|
||||||
None)])
|
tenant='tenantA',
|
||||||
mock_grant_role.assert_has_calls([call('userA', 'roleA', 'tenantA',
|
domain=None)])
|
||||||
None)])
|
mock_grant_role.assert_has_calls([call('userA', 'roleA',
|
||||||
|
tenant='tenantA',
|
||||||
|
user_domain=None,
|
||||||
|
project_domain=None)])
|
||||||
mock_update_user_password.assert_has_calls([call('userA', 'passA')])
|
mock_update_user_password.assert_has_calls([call('userA', 'passA')])
|
||||||
|
|
||||||
@patch.object(utils, 'get_manager')
|
@patch.object(utils, 'get_manager')
|
||||||
@@ -1079,7 +1107,7 @@ class TestKeystoneUtils(CharmTestCase):
|
|||||||
create_user_credentials.assert_called_with('requester', 'password',
|
create_user_credentials.assert_called_with('requester', 'password',
|
||||||
domain='Non-Default',
|
domain='Non-Default',
|
||||||
new_roles=[],
|
new_roles=[],
|
||||||
grants=['Admin'],
|
grants=['service'],
|
||||||
tenant='services')
|
tenant='services')
|
||||||
self.peer_store_and_set.assert_called_with(relation_id=relation_id,
|
self.peer_store_and_set.assert_called_with(relation_id=relation_id,
|
||||||
**relation_data)
|
**relation_data)
|
||||||
@@ -1130,7 +1158,7 @@ class TestKeystoneUtils(CharmTestCase):
|
|||||||
utils.add_credentials_to_keystone(
|
utils.add_credentials_to_keystone(
|
||||||
relation_id=relation_id,
|
relation_id=relation_id,
|
||||||
remote_unit=remote_unit)
|
remote_unit=remote_unit)
|
||||||
create_tenant.assert_called_with('myproject')
|
create_tenant.assert_called_with('myproject', None)
|
||||||
create_user_credentials.assert_called_with('requester', 'password',
|
create_user_credentials.assert_called_with('requester', 'password',
|
||||||
domain=None,
|
domain=None,
|
||||||
new_roles=['New', 'Member'],
|
new_roles=['New', 'Member'],
|
||||||
|
Reference in New Issue
Block a user