我有一个相当复杂的问题,我可以提供一些帮助,因为我一直在尝试的所有实现都是有缺陷的(Rails 4.2.1)。
用户可以访问设备:
create_table "devices", force: :cascade do |t|
t.string "device_guid"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "user_ids"
end
并且设备有许多数据条目:
create_table "datas", force: :cascade do |t|
t.string "device_id"
t.datetime "start_time"
t.datetime "end_time"
end
我们希望根据订阅将所有查询限制为数据。
create_table "subscriptions", force: :cascade do |t|
t.integer "device_id"
t.integer "user_id"
t.datetime "start_date"
t.datetime "end_date"
end
如此有效,除非订阅允许,否则不应访问数据。
我希望在一个地方完成此限制(不是通过单独的方法来访问它),因此编写代码的其他人仍然处理订阅限制。我不想使用第三方gem,因为数据非常敏感。
我认为最明显的方法是在Data模型上设置default_scope,但我遇到了几个问题:
1)您无法从数据模型访问@user。我用一个丑陋的黑客来解决这个问题,从模型中访问User.current
2)我无法使用current_user,因为我们的管理员构成其他用户,并且需要按原样查看数据。使用与#1相同的解决方法
3)同一个用户和设备可能有多个具有不同日期的订阅。没有办法做一个我知道的ActiveRecord查询可以解释两个不同的日期范围(我通过使用arel_table解决了这个问题)。
4)当我在rails控制台(理论上通过API接口)时,这使得查询数据的所有方法都失败了。如果没有User.current,我忽略了默认范围,但是很明显这是一个安全问题。
这是我当前的实现(在psuedocode中):
query = []
Subscription.each do |s|
if (User.current.id == s.user_id)
temp = Data.all.where(data[:start_time] >= s.start_date).where(data[:end_time] <= s.end_date).where(data[:device_id] == Device.find_by(id: s.device_id).device_guid)
end
end
query += temp
end
default_scope { query }
我将此代码直接放入模型中。我认为它不会起作用,但它确实......有时候。它行为奇怪,有时完全限制用户不应该,尽管它还没有(但)给某人访问他们不应该拥有的数据。
我正在寻求从头开始重新设计,并以最好的方式做到这一点。关于如何最好地接近它,我愿意接受任何建议(第三方除外,抱歉)。我不介意实施需要做很多工作,我宁愿这次做对。任何可扩展性帐户(例如,如果我们为远程查询实现了API)都将是一个很好的功能,尽管我们目前不需要它。
重申一下,我坚持认为这个限制对所有查询都是“自动的”,所以如果前编码器例如写道:
<%= @user.device.find(1).data.all.count %>
在视图中,它不会显示用户没有订阅的任何数据。
感谢您的任何建议。对于长篇文章感到抱歉,这是一个相当令人困惑的问题。