diff --git a/etc/nova/policy.json b/etc/nova/policy.json index 8c19844d40c..c5e7c57cd56 100644 --- a/etc/nova/policy.json +++ b/etc/nova/policy.json @@ -1,6 +1,6 @@ { "context_is_admin": "role:admin", - "admin_or_owner": "is_admin:True or project_id:%(project_id)s", + "admin_or_owner": "is_admin:True or prefix:project_id:%(project_id)s", "default": "rule:admin_or_owner", "cells_scheduler_filter:TargetCellFilter": "is_admin:True", diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 41ab1b7631f..55ea63121f6 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -224,13 +224,14 @@ def issubclassof_nova_base(obj): raise Exception(_("Unrecognized read_deleted value '%s'") % read_deleted) + like = '%s%%' % context.project_id if nova.context.is_user_context(context) and project_only: if project_only == 'allow_none': query = query.\ - filter(or_(base_model.project_id == context.project_id, + filter(or_(base_model.project_id.like(like), base_model.project_id == None)) else: - query = query.filter_by(project_id=context.project_id) + query = query.filter(base_model.project_id.like(like)) return query @@ -1931,11 +1932,14 @@ def instance_get_all_by_filters(context, filters, sort_key, sort_dir, # Filters for exact matches that we can do along with the SQL query... # For other filters that don't match this, we will do regexp matching - exact_match_filter_names = ['project_id', 'user_id', 'image_ref', + exact_match_filter_names = ['user_id', 'image_ref', 'vm_state', 'instance_type_id', 'uuid', 'metadata', 'host', 'task_state', 'system_metadata'] + query_prefix = prefix_filter(query_prefix, models.Instance, + {'project_id': filters['project_id']}) + filters.pop('project_id') # Filter the query query_prefix = exact_filter(query_prefix, models.Instance, filters, exact_match_filter_names) @@ -2051,6 +2055,28 @@ def regex_filter(query, model, filters): str(filters[filter_name]))) return query +def prefix_filter(query, model, filters): + """Applies prefix filtering to a query. + + Returns the updated query. + + :param query: query to apply filters to + :param model: model object the query applies to + :param filters: dictionary of filters with string prefixes + """ + + for filter_name in filters.iterkeys(): + try: + column_attr = getattr(model, filter_name) + except AttributeError: + continue + if 'property' == type(column_attr).__name__: + continue + like = '%s%%' % filters[filter_name] + query = query.filter(column_attr.like(like)) + return query + + @require_context def instance_get_active_by_window_joined(context, begin, end=None, diff --git a/nova/openstack/common/policy.py b/nova/openstack/common/policy.py index c15c0d5fe12..2697b6a1293 100644 --- a/nova/openstack/common/policy.py +++ b/nova/openstack/common/policy.py @@ -742,6 +742,17 @@ def __call__(self, target, creds): return self.match.lower() in [x.lower() for x in creds['roles']] +@register("prefix") +class PrefixCheck(Check): + def __call__(self, target, creds): + """Check that the target starts with item in the creds dict.""" + source, _, match = self.match.partition(':') + match = match % target + if source in creds: + return match.startswith(unicode(creds[source])) + return False + + @register('http') class HttpCheck(Check): def __call__(self, target, creds):