【问题标题】:What is the best way pre filter user access for sqlalchemy queries?为 sqlalchemy 查询预过滤用户访问权限的最佳方法是什么?
【发布时间】:2010-05-21 20:41:08
【问题描述】:

我一直在他们的 wiki 上查看 sqlalchemy 食谱,但不知道哪一个最适合实现我正在尝试做的事情。

我表中的每一行都有一个与之关联的 user_id。现在,对于每一个查询,我都是通过当前登录的用户的 id 进行查询,然后根据我感兴趣的条件进行查询。我担心开发人员可能会忘记将这个过滤器添加到查询中(一个巨大的安全风险)。因此,我想根据当前用户的管理员权限设置一个全局过滤器,以过滤登录用户可以看到的内容。

感谢您的帮助。谢谢。

【问题讨论】:

标签: filter sqlalchemy acl


【解决方案1】:

下面是简化的重新定义的查询构造函数,用于过滤所有模型查询(包括关系)。您可以将其作为query_cls 参数传递给sessionmaker。用户 ID 参数不需要是全局的,只要会话在可用时构建。

class HackedQuery(Query):

    def get(self, ident):
        # Use default implementation when there is no condition
        if not self._criterion:
            return Query.get(self, ident)
        # Copied from Query implementation with some changes.
        if hasattr(ident, '__composite_values__'):
            ident = ident.__composite_values__()
        mapper = self._only_mapper_zero(
                    "get() can only be used against a single mapped class.")
        key = mapper.identity_key_from_primary_key(ident)
        if ident is None:
            if key is not None:
                ident = key[1]
        else:
            from sqlalchemy import util
            ident = util.to_list(ident)
        if ident is not None:
            columns = list(mapper.primary_key)
            if len(columns)!=len(ident):
                raise TypeError("Number of values doen't match number "
                                'of columns in primary key')
            params = {}
            for column, value in zip(columns, ident):
                params[column.key] = value
            return self.filter_by(**params).first()


def QueryPublic(entities, session=None):
    # It's not directly related to the problem, but is useful too.
    query = HackedQuery(entities, session).with_polymorphic('*')
    # Version for several entities needs thorough testing, so we 
    # don't use it yet.
    assert len(entities)==1, entities
    cls = _class_to_mapper(entities[0]).class_
    public_condition = getattr(cls, 'public_condition', None)
    if public_condition is not None:
        query = query.filter(public_condition)
    return query

它只适用于单一模型查询,还有很多工作使它适用于其他情况。我希望看到一个详细的版本,因为它必须具有大多数 Web 应用程序的功能。它使用存储在每个模型类中的固定条件,因此您必须根据需要对其进行修改。

【讨论】:

  • 所以我最终使用sqlalchemy.org/trac/wiki/UsageRecipes/PreFilteredQuery 的组合来跟踪您的答案。有没有办法像 sessionmaker 那样在模型类级别指定 query_cls ?并非所有模型都有我要过滤的此列。
  • 哦,我在模型中做了 query = Session.query_property(FilterByUserQuery)。非常感谢。
【解决方案2】:

这是一个非常幼稚的实现,它假定登录用户已存储属性/属性self.current_user

class YourBaseRequestHandler(object):

    @property
    def current_user(self):
        """The current user logged in."""
        pass

    def query(self, session, entities):
        """Use this method instead of :method:`Session.query()
        <sqlalchemy.orm.session.Session.query>`.

        """
        return session.query(entities).filter_by(user_id=self.current_user.id)

【讨论】:

    【解决方案3】:

    我写了一个 SQLAlchemy 扩展,我认为它符合您的描述:https://github.com/mwhite/multialchemy

    它通过代理对 Query._from_objQueryContext._froms 属性的更改来实现这一点,这是最终设置要从中选择的表的位置。

    【讨论】:

      猜你喜欢
      • 2012-03-22
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 1970-01-01
      • 2012-12-09
      • 2011-10-28
      • 2014-02-21
      • 2011-02-17
      相关资源
      最近更新 更多