def query_with_hooks(context, model):
    query = context.session.query(model)
    # define basic filter condition for model query
    query_filter = None
    if ndb_utils.model_query_scope_is_project(context, model):
        if hasattr(model, 'rbac_entries'):
            query = query.outerjoin(model.rbac_entries)
            rbac_model = model.rbac_entries.property.mapper.class_
            query_filter = (
                (model.tenant_id == context.tenant_id) |
                ((rbac_model.action == 'access_as_shared') &
                 ((rbac_model.target_tenant == context.tenant_id) |
                  (rbac_model.target_tenant == '*'))))
        elif hasattr(model, 'shared'):
            query_filter = ((model.tenant_id == context.tenant_id) |
                            (model.shared == sql.true()))
        else:
            query_filter = (model.tenant_id == context.tenant_id)
    # Execute query hooks registered from mixins and plugins
    for hook in get_hooks(model):
        query_hook = helpers.resolve_ref(hook.get('query'))
        if query_hook:
            query = query_hook(context, model, query)

        filter_hook = helpers.resolve_ref(hook.get('filter'))
        if filter_hook:
            query_filter = filter_hook(context, model, query_filter)

    # NOTE(salvatore-orlando): 'if query_filter' will try to evaluate the
    # condition, raising an exception
    if query_filter is not None:
        query = query.filter(query_filter)
    return query
 def _port_filter_hook(context, original_model, conditions):
     # Apply the port filter only in non-admin and non-advsvc context
     if db_utils.model_query_scope_is_project(context, original_model):
         conditions |= (
             (context.tenant_id == models_v2.Network.tenant_id) &
             (models_v2.Network.id == models_v2.Port.network_id))
     return conditions
Beispiel #3
0
    def _model_query(self, context, model):
        query = context.session.query(model)
        # define basic filter condition for model query
        query_filter = None
        if ndb_utils.model_query_scope_is_project(context, model):
            if hasattr(model, 'rbac_entries'):
                query = query.outerjoin(model.rbac_entries)
                rbac_model = model.rbac_entries.property.mapper.class_
                query_filter = ((model.tenant_id == context.tenant_id) | (
                    (rbac_model.action == 'access_as_shared') &
                    ((rbac_model.target_tenant == context.tenant_id) |
                     (rbac_model.target_tenant == '*'))))
            elif hasattr(model, 'shared'):
                query_filter = ((model.tenant_id == context.tenant_id) |
                                (model.shared == sql.true()))
            else:
                query_filter = (model.tenant_id == context.tenant_id)
        # Execute query hooks registered from mixins and plugins
        for _name, hooks in six.iteritems(
                self._model_query_hooks.get(model, {})):
            query_hook = self._resolve_ref(hooks.get('query'))
            if query_hook:
                query = query_hook(context, model, query)

            filter_hook = self._resolve_ref(hooks.get('filter'))
            if filter_hook:
                query_filter = filter_hook(context, model, query_filter)

        # NOTE(salvatore-orlando): 'if query_filter' will try to evaluate the
        # condition, raising an exception
        if query_filter is not None:
            query = query.filter(query_filter)
        return query
Beispiel #4
0
def apply_filters(query, model, filters, context=None):
    if filters:
        for key, value in filters.items():
            column = getattr(model, key, None)
            # NOTE(kevinbenton): if column is a hybrid property that
            # references another expression, attempting to convert to
            # a boolean will fail so we must compare to None.
            # See "An Important Expression Language Gotcha" in:
            # docs.sqlalchemy.org/en/rel_0_9/changelog/migration_06.html
            if column is not None:
                if not value:
                    query = query.filter(sql.false())
                    return query
                if isinstance(column, associationproxy.AssociationProxy):
                    # association proxies don't support in_ so we have to
                    # do multiple equals matches
                    query = query.filter(or_(*[column == v for v in value]))
                else:
                    query = query.filter(column.in_(value))
            elif key == 'shared' and hasattr(model, 'rbac_entries'):
                # translate a filter on shared into a query against the
                # object's rbac entries
                rbac = model.rbac_entries.property.mapper.class_
                matches = [rbac.target_tenant == '*']
                if context:
                    matches.append(rbac.target_tenant == context.tenant_id)
                # any 'access_as_shared' records that match the
                # wildcard or requesting tenant
                is_shared = and_(rbac.action == 'access_as_shared',
                                 or_(*matches))
                if not value[0]:
                    # NOTE(kevinbenton): we need to find objects that don't
                    # have an entry that matches the criteria above so
                    # we use a subquery to exclude them.
                    # We can't just filter the inverse of the query above
                    # because that will still give us a network shared to
                    # our tenant (or wildcard) if it's shared to another
                    # tenant.
                    # This is the column joining the table to rbac via
                    # the object_id. We can't just use model.id because
                    # subnets join on network.id so we have to inspect the
                    # relationship.
                    join_cols = model.rbac_entries.property.local_columns
                    oid_col = list(join_cols)[0]
                    is_shared = ~oid_col.in_(
                        query.session.query(rbac.object_id).filter(is_shared))
                elif (not context
                      or not ndb_utils.model_query_scope_is_project(
                          context, model)):
                    # we only want to join if we aren't using the subquery
                    # and if we aren't already joined because this is a
                    # scoped query
                    query = query.outerjoin(model.rbac_entries)
                query = query.filter(is_shared)
        for hook in get_hooks(model):
            result_filter = utils.resolve_ref(hook.get('result_filters', None))
            if result_filter:
                query = result_filter(query, filters)
    return query
 def _port_filter_hook(context, original_model, conditions):
     # Apply the port filter only in non-admin and non-advsvc context
     if db_utils.model_query_scope_is_project(context, original_model):
         conditions |= (models_v2.Port.network_id.in_(
             context.session.query(models_v2.Network.id).filter(
                 context.project_id ==
                 models_v2.Network.project_id).subquery()))
     return conditions
 def _port_query_hook(context, original_model, query):
     # we need to outerjoin to networks if the model query scope
     # is necessary so we can filter based on network id. without
     # this the conditions in the filter hook cause the networks
     # table to be added to the FROM statement so we get lots of
     # duplicated rows that break the COUNT operation
     if db_utils.model_query_scope_is_project(context, original_model):
         query = query.outerjoin(models_v2.Network)
     return query
Beispiel #7
0
def _network_filter_hook(context, original_model, conditions):
    if conditions is not None and not hasattr(conditions, '__iter__'):
        conditions = (conditions, )
    # Apply the external network filter only in non-admin and non-advsvc
    # context
    if db_utils.model_query_scope_is_project(context, original_model):
        # the table will already be joined to the rbac entries for the
        # shared check so we don't need to worry about ensuring that
        rbac_model = original_model.rbac_entries.property.mapper.class_
        tenant_allowed = ((rbac_model.action == 'access_as_external') &
                          (rbac_model.target_tenant == context.tenant_id) |
                          (rbac_model.target_tenant == '*'))
        conditions = expr.or_(tenant_allowed, *conditions)
    return conditions
Beispiel #8
0
def _network_filter_hook(context, original_model, conditions):
    if conditions is not None and not hasattr(conditions, '__iter__'):
        conditions = (conditions, )
    # Apply the external network filter only in non-admin and non-advsvc
    # context
    if db_utils.model_query_scope_is_project(context, original_model):
        # the table will already be joined to the rbac entries for the
        # shared check so we don't need to worry about ensuring that
        rbac_model = original_model.rbac_entries.property.mapper.class_
        tenant_allowed = (
            (rbac_model.action == 'access_as_external') &
            (rbac_model.target_tenant == context.tenant_id) |
            (rbac_model.target_tenant == '*'))
        conditions = expr.or_(tenant_allowed, *conditions)
    return conditions
Beispiel #9
0
    def _model_query(self, context, model):
        query = context.session.query(model)
        # define basic filter condition for model query
        query_filter = None
        if ndb_utils.model_query_scope_is_project(context, model):
            if hasattr(model, 'rbac_entries'):
                query = query.outerjoin(model.rbac_entries)
                rbac_model = model.rbac_entries.property.mapper.class_
                query_filter = (
                    (model.tenant_id == context.tenant_id) |
                    ((rbac_model.action == 'access_as_shared') &
                     ((rbac_model.target_tenant == context.tenant_id) |
                      (rbac_model.target_tenant == '*'))))
            elif hasattr(model, 'shared'):
                query_filter = ((model.tenant_id == context.tenant_id) |
                                (model.shared == sql.true()))
            else:
                query_filter = (model.tenant_id == context.tenant_id)
        # Execute query hooks registered from mixins and plugins
        for _name, hooks in six.iteritems(self._model_query_hooks.get(model,
                                                                      {})):
            query_hook = hooks.get('query')
            if isinstance(query_hook, six.string_types):
                query_hook = getattr(self, query_hook, None)
            if query_hook:
                query = query_hook(context, model, query)

            filter_hook = hooks.get('filter')
            if isinstance(filter_hook, six.string_types):
                filter_hook = getattr(self, filter_hook, None)
            if filter_hook:
                query_filter = filter_hook(context, model, query_filter)

        # NOTE(salvatore-orlando): 'if query_filter' will try to evaluate the
        # condition, raising an exception
        if query_filter is not None:
            query = query.filter(query_filter)
        return query
Beispiel #10
0
 def model_query_scope(self, context, model):
     return ndb_utils.model_query_scope_is_project(context, model)
Beispiel #11
0
 def model_query_scope(self, context, model):
     return ndb_utils.model_query_scope_is_project(context, model)
def apply_filters(query, model, filters, context=None):
    if filters:
        for key, value in filters.items():
            column = getattr(model, key, None)
            # NOTE(kevinbenton): if column is a hybrid property that
            # references another expression, attempting to convert to
            # a boolean will fail so we must compare to None.
            # See "An Important Expression Language Gotcha" in:
            # docs.sqlalchemy.org/en/rel_0_9/changelog/migration_06.html
            if column is not None:
                if not value:
                    query = query.filter(sql.false())
                    return query
                if isinstance(column, associationproxy.AssociationProxy):
                    # association proxies don't support in_ so we have to
                    # do multiple equals matches
                    query = query.filter(
                        or_(*[column == v for v in value]))
                elif isinstance(value, obj_utils.StringMatchingFilterObj):
                    if value.is_contains:
                        query = query.filter(
                            column.contains(value.contains))
                    elif value.is_starts:
                        query = query.filter(
                            column.startswith(value.starts))
                    elif value.is_ends:
                        query = query.filter(
                            column.endswith(value.ends))
                elif None in value:
                    # in_() operator does not support NULL element so we have
                    # to do multiple equals matches
                    query = query.filter(
                        or_(*[column == v for v in value]))
                else:
                    query = query.filter(column.in_(value))
            elif key == 'shared' and hasattr(model, 'rbac_entries'):
                # translate a filter on shared into a query against the
                # object's rbac entries
                rbac = model.rbac_entries.property.mapper.class_
                matches = [rbac.target_tenant == '*']
                if context:
                    matches.append(rbac.target_tenant == context.tenant_id)
                # any 'access_as_shared' records that match the
                # wildcard or requesting tenant
                is_shared = and_(rbac.action == 'access_as_shared',
                                 or_(*matches))
                if not value[0]:
                    # NOTE(kevinbenton): we need to find objects that don't
                    # have an entry that matches the criteria above so
                    # we use a subquery to exclude them.
                    # We can't just filter the inverse of the query above
                    # because that will still give us a network shared to
                    # our tenant (or wildcard) if it's shared to another
                    # tenant.
                    # This is the column joining the table to rbac via
                    # the object_id. We can't just use model.id because
                    # subnets join on network.id so we have to inspect the
                    # relationship.
                    join_cols = model.rbac_entries.property.local_columns
                    oid_col = list(join_cols)[0]
                    is_shared = ~oid_col.in_(
                        query.session.query(rbac.object_id).filter(is_shared)
                    )
                elif (not context or
                      not ndb_utils.model_query_scope_is_project(context,
                                                                 model)):
                    # we only want to join if we aren't using the subquery
                    # and if we aren't already joined because this is a
                    # scoped query
                    query = query.outerjoin(model.rbac_entries)
                query = query.filter(is_shared)
        for hook in get_hooks(model):
            result_filter = helpers.resolve_ref(
                hook.get('result_filters', None))
            if result_filter:
                query = result_filter(query, filters)
    return query