Python 基于值组合的安全散列密钥
我在MongoDB中有大量记录/文档,我需要通过每个文档列表中的值组合来限制对项目的访问 想象一下,安全性可能的单个值是[1,2,3] 一个记录可以包含以下内容的任意组合: (1,)(2,)(3,)(1,2)(1,3)(2,3)(1,2,3)Python 基于值组合的安全散列密钥,python,mongodb,pymongo,Python,Mongodb,Pymongo,我在MongoDB中有大量记录/文档,我需要通过每个文档列表中的值组合来限制对项目的访问 想象一下,安全性可能的单个值是[1,2,3] 一个记录可以包含以下内容的任意组合: (1,)(2,)(3,)(1,2)(1,3)(2,3)(1,2,3) 有权访问[1]的用户只能看到具有()和(1)的记录 有权访问[2]的用户只能看到具有()和(2)的记录 有权访问[1,2]的用户将只能看到具有(),(1),(2),(1,2)的记录 只有能够访问[1,2,3]的用户才能查看所有记录 现在,在数据库的入口
- 有权访问[1]的用户只能看到具有()和(1)的记录
- 有权访问[2]的用户只能看到具有()和(2)的记录
- 有权访问[1,2]的用户将只能看到具有(),(1),(2),(1,2)的记录
- 只有能够访问[1,2,3]的用户才能查看所有记录
def hash_combination(input):
return hash(frozenset(input))
这将为每个记录提供一个唯一的键,我们可以将其作为过滤器索引使用。然后,为用户获取所有可能的密钥也很容易:
from itertools import chain, combinations
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1))
def hash_powerset(iterable):
return [hash(frozenset(x)) for x in powerset(iterable)]
但是,组合可能的唯一输入的实际列表可能相当大(50+),这造成了一个太大而不实用的因素
我只能想到两种可能的解决办法。第一是逐行检查:
security_list = (1, 2, 3)
for row in db.collection.find():
# check security
if any(x not in security_list for x in row['row_security']):
continue
# security passed
pass
但这是一个相当高的性能杀手。另一个是将选择反转为“我们看不见的东西”:
但这也是mongodb无法索引的操作(可能是因为与我现在遇到的类似的原因),因此性能仍然不好。比前面的选项更好(因为您避免了将python对象转换为瓶颈),但仍然不是很好
我们案例的一些细节:
- 我们总是知道用户的安全列表
- 我们总是知道唯一的可能值列表(这可能很大)
- python 2.7、mongodb 3.0
Carst根据Python Zen,您首先要发明一个干净的解决方案,只有当您真正看到它需要优化时,才着手对其进行优化 如你所见,这里实际上有两个任务:1)制定一个通用算法,2)针对特定环境优化算法
您任务的核心是:
- 鉴于:
- 每个记录都有一组标记(1,2,3),标记访问它所需的“特权/许可级别”
- 用户也有一组相同的标志,指定其清除级别
- 问题:
- 返回用户有权访问的所有记录
result = {record for record in set_ if user.mask >= record.mask}
现在,对于第二个任务,您需要检查MongoDB有效地执行了哪些操作,并找出如何使用它们来实现此操作。对于代码审查来说,这似乎是一个很好的问题()老实说,我发现有时很难选择哪一个是最好的。但是我基于它指出stackoverflow是“一个特定的编程问题,或者一个软件算法”,我想这是你发明的系统的正确名称。嗨,完全同意Python Zen,我必须强调,我给出的两个解决方案在功能上都是有效的。第一个解决方案基本上是您建议的真实mongodb版本。我必须说,我的问题的标题可能会更好。问题是:算法类型和掩码的包含/排除方法在这里非常重要,这就是为什么我在每次解决方案之后都会深入到性能部分。这使得两种理论上的功能性解决方案在实用上都不可行:/
result = {record for record in set_ if user.mask >= record.mask}