deb-sahara/sahara/utils/proxy.py
Michael McCune f1facb74ae Adding a periodic task to remove zombie proxy users
Changes
* adding periodic task for user removal
* adding a wrapper function for the periodic tasks class to improve
  configuration
* refactoring proxy user delete function to allow a user id

Change-Id: I641e1650e7a5fcd96246e13b7e1d548c4a0dda25
Partial-implements: blueprint edp-swift-trust-authentication
2014-09-09 09:16:44 -04:00

212 lines
7.9 KiB
Python

# Copyright (c) 2014 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import uuid
from oslo.config import cfg
import six
from sahara import conductor as c
from sahara import context
from sahara import exceptions as ex
from sahara.i18n import _
from sahara.openstack.common import log as logging
from sahara.service import trusts as t
from sahara.swift import utils as su
from sahara.utils.openstack import keystone as k
PROXY_DOMAIN = None
conductor = c.API
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
opts = [
cfg.BoolOpt('use_domain_for_proxy_users',
default=False,
help='Enables Sahara to use a domain for creating temporary '
'proxy users to access Swift. If this is enabled '
'a domain must be created for Sahara to use.'),
cfg.StrOpt('proxy_user_domain_name',
default=None,
help='The domain Sahara will use to create new proxy users '
'for Swift object access.'),
cfg.ListOpt('proxy_user_role_names',
default=['Member'],
help='A list of the role names that the proxy user should '
'assume through trust for Swift object access.')
]
CONF.register_opts(opts)
def create_proxy_user_for_job_execution(job_execution):
'''Creates a proxy user and adds the credentials to the job execution
:param job_execution: The job execution model to update
'''
username = 'job_{0}'.format(job_execution.id)
password = proxy_user_create(username)
current_user = k.client()
proxy_user = k.client_for_proxy_user(username, password)
trust_id = t.create_trust(trustor=current_user,
trustee=proxy_user,
role_names=CONF.proxy_user_role_names)
update = {'job_configs': job_execution.job_configs.to_dict()}
update['job_configs']['proxy_configs'] = {
'proxy_username': username,
'proxy_password': password,
'proxy_trust_id': trust_id
}
conductor.job_execution_update(context.ctx(), job_execution, update)
def delete_proxy_user_for_job_execution(job_execution):
'''Delete a proxy user based on a JobExecution
:param job_execution: The job execution with proxy user information
:returns: An updated job_configs dictionary or None
'''
proxy_configs = job_execution.job_configs.get('proxy_configs')
if proxy_configs is not None:
proxy_username = proxy_configs.get('proxy_username')
proxy_password = proxy_configs.get('proxy_password')
proxy_trust_id = proxy_configs.get('proxy_trust_id')
proxy_user = k.client_for_proxy_user(proxy_username,
proxy_password,
proxy_trust_id)
t.delete_trust(proxy_user, proxy_trust_id)
proxy_user_delete(proxy_username)
update = {'job_configs': job_execution.job_configs.to_dict()}
del update['job_configs']['proxy_configs']
return update
return None
def domain_for_proxy():
'''Return the proxy domain or None
If configured to use the proxy domain, this function will return that
domain. If not configured to use the proxy domain, this function will
return None. If the proxy domain can't be found this will raise an
exception.
:returns: A Keystone Domain object or None.
:raises ConfigurationError: If the domain is requested but not specified.
:raises NotFoundException: If the domain name is specified but cannot be
found.
'''
if CONF.use_domain_for_proxy_users is False:
return None
if CONF.proxy_user_domain_name is None:
raise ex.ConfigurationError(_('Proxy domain requested but not '
'specified.'))
admin = k.client_for_admin()
global PROXY_DOMAIN
if not PROXY_DOMAIN:
domain_list = admin.domains.list(name=CONF.proxy_user_domain_name)
if len(domain_list) == 0:
raise ex.NotFoundException(value=CONF.proxy_user_domain_name,
message=_('Failed to find domain %s'))
# the domain name should be globally unique in Keystone
if len(domain_list) > 1:
raise ex.NotFoundException(value=CONF.proxy_user_domain_name,
message=_('Unexpected results found '
'when searching for domain '
'%s'))
PROXY_DOMAIN = domain_list[0]
return PROXY_DOMAIN
def job_execution_requires_proxy_user(job_execution):
'''Returns True if the job execution requires a proxy user.'''
if CONF.use_domain_for_proxy_users is False:
return False
input_ds = conductor.data_source_get(context.ctx(),
job_execution.input_id)
if input_ds and input_ds.url.startswith(su.SWIFT_INTERNAL_PREFIX):
return True
output_ds = conductor.data_source_get(context.ctx(),
job_execution.output_id)
if output_ds and output_ds.url.startswith(su.SWIFT_INTERNAL_PREFIX):
return True
if job_execution.job_configs.get('args'):
for arg in job_execution.job_configs['args']:
if arg.startswith(su.SWIFT_INTERNAL_PREFIX):
return True
job = conductor.job_get(context.ctx(), job_execution.job_id)
for main in job.mains:
if main.url.startswith(su.SWIFT_INTERNAL_PREFIX):
return True
for lib in job.libs:
if lib.url.startswith(su.SWIFT_INTERNAL_PREFIX):
return True
return False
def proxy_domain_users_list():
'''Return a list of all users in the proxy domain.'''
admin = k.client_for_admin()
domain = domain_for_proxy()
if domain:
return admin.users.list(domain=domain.id)
return []
def proxy_user_create(username):
'''Create a new user in the proxy domain
Creates the username specified with a random password.
:param username: The name of the new user.
:returns: The password created for the user.
'''
admin = k.client_for_admin()
domain = domain_for_proxy()
password = six.text_type(uuid.uuid4())
admin.users.create(name=username, password=password, domain=domain.id)
LOG.debug(_('created proxy user {0}').format(username))
return password
def proxy_user_delete(username=None, user_id=None):
'''Delete the user from the proxy domain.
:param username: The name of the user to delete.
:param user_id: The id of the user to delete, if provided this overrides
the username.
:raises NotFoundException: If there is an error locating the user in the
proxy domain.
'''
admin = k.client_for_admin()
if not user_id:
domain = domain_for_proxy()
user_list = admin.users.list(domain=domain.id, name=username)
if len(user_list) == 0:
raise ex.NotFoundException(value=username,
message=_('Failed to find user %s'))
if len(user_list) > 1:
raise ex.NotFoundException(value=username,
message=_('Unexpected results found '
'when searching for user %s'))
user_id = user_list[0].id
admin.users.delete(user_id)
LOG.debug('deleted proxy user id {0}'.format(user_id))