【问题标题】:Allowing only certain values though a strong parameter in Rails 4通过 Rails 4 中的强参数仅允许某些值
【发布时间】:2023-03-15 05:18:01
【问题描述】:

我有一个字段 otp_set_up,在 company_user 模型中允许为“true”或“false”。

有一个用例,系统管理员用户可以将此字段重置为“false”。

虽然可以通过代码将该字段设置为“true”,但没有用户可以通过表单编辑等将其设置为“true”。

我没有在模型中添加验证,因为它可以是“真”或“假”。

我在 params.require .permit 位之前的特定于控制器更新的 params 方法中有以下代码:

if curr_company_user.is_sys_admin? && curr_company_user.can_crud_company_users? && params[:id].to_i != curr_company_user.id

  params[:company_user] = params[:company_user].except(:otp_set_up) if params[:company_user][:otp_set_up] == true
  params.require(:company_user).permit(:otp_setup, etc. etc....

elsif etc. etc...

这行得通。系统管理员用户不能将 otp_set_up 设置为“true”。

我的问题是:

这是在 Rails 中最好和正确的方法吗?对我来说这似乎有点 hacky,通过 params 哈希并删除一点。

有没有更好/更清洁的方法?

【问题讨论】:

  • 您是通过视图或控制器中的代码还是仅通过模型将值设置为“true”?
  • 这实际上还没有实现!但应在 otp_setup 控制器创建方法中设置为“true”。

标签: ruby-on-rails ruby-on-rails-4 strong-parameters


【解决方案1】:

delete_if 清理它。仍然有点hacky,但稍微少一点:)

params.require(:company_user).permit(:otp_setup).delete_if do |key, val|
  key == 'otp_setup' && val == true
end

这使原始params 对象保持不变。

没有内置的方法可以做到这一点。好像以前有,但现在没有了https://github.com/rails/strong_parameters/issues/167

delete_if 是在核心库中的Hash 上定义的,因此在没有内置方法的情况下,这可能是在 Ruby 中以及在 Rails 中扩展的最佳方法。

更新

我认为这是一个有趣的想法,所以我为这种类型的用例编写了一个名为 allowable 的小 gem。它将为HashActionController::Parameters添加一些方法:#allow#allow!#forbid#forbid!

你会这样使用它

params.require(:company_user).permit(:otp_setup).forbid(otp_setup: [true])

# or

params.require(:company_user).permit(:otp_setup).allow(otp_setup: [false])

您可以指定单个值或值数组,它不会改变原始params 对象

【讨论】:

  • 谢谢。我决定接受您的 delete_if 建议。在 gem 上做的很好,不幸的是我不能在生产系统的工作中使用它(不允许)。可惜他们没有为 ActionController::Parameters 采纳你的功能建议。在我看来,在这种情况下它属于哪里。
  • @Iain 当然没问题。随意查看我在 gem 中使用的实际实现,它比 #delete_if 更快
  • 就像循环中的“delete(key)”而不是循环的def之前的“delete_if”?
  • 我现在需要从多个参数中剔除值,所以我要粘贴多个参数的代码。注意线末端的点以链接在一起。我花了一段时间才弄清楚,因为我对 Rails 很陌生。 params.require(:user).permit(:failed_login_count, :active, :otp_setup, :otherthings).delete_if do |key, val| key == 'failed_login_count' && val.to_i != 0end.delete_if do |key, val| key == 'active' && val == 'false'end.delete_if do |key, val| key == 'otp_setup' && val == 'true'end
  • 这是一个不错的 gem,将丑陋的 delete_if 格式排除在控制器之外
【解决方案2】:

在这种情况下,我真的不建议使用 params 对象。我认为最好保留大部分内容以保留实际请求的内容。这样,如果您在下游某处再次需要该值,您就不会摸不着头脑。

另一种方法是在传递到permit 之前构建要接受的属性列表。

# Attributes that everyone can modify.
attrs = [:attrs, :everyone, :can, :modify]

# Then "whitelist" other attributes based on your permission logic.
if curr_company_user.is_sys_admin? && curr_company_user.can_crud_company_users? && params[:id].to_i != curr_company_user.id
  attrs << :otp_set_up unless params[:company_user][:otp_set_up] == true
elsif something_else?
  # Modify what can be permitted for this case.
  # etc...
end

params.require(:company_user).permit(*attrs)

【讨论】:

  • 感谢您的回复,但我更喜欢另一个答案中的 delete_if 建议。看起来有点整洁,但是您不修改 params 对象的建议都是可行的方法!
  • @Iain 没问题。我想在某些情况下delete_if 会不那么整洁。 (此外,我总是更喜欢白名单方法而不是黑名单。)我假设那里的其他人可能与我有相同的偏好,所以我会留下我的答案作为替代方案。不过,很高兴您找到了针对您的特定问题的最佳解决方案。
【解决方案3】:

我建议您仅在用户是管理员时才在参数中设置它,否则不要。我认为这是一个更好的方法。

在模型中,执行如下操作:

if user.role == 'admin'
  attr_accessor #All the params
else
  attr_accessor #All the other params except the one you want to 
  exclude

【讨论】:

  • 这实际上已经在我的代码中完成了。我只是在原始问题中没有它。我现在添加它以使其更清晰。谢谢。
  • 在模型中设置强参数时。那是您为 otp_set_up 设置参数的时候。
  • 它们应该设置在控制器中吗?那就是它们所在的位置,但是我寻找了一种仅允许某些值进入的方法,但找不到解决方案。例如类似于 params.require(:company_user).permit(:otp_setup => false 和许多不同的变体,但没有任何效果。
  • 问题是,我只希望系统管理员能够将 otp_set_up 设置为“false”。这就是为什么我目前去掉了一个“真实”的价值。
猜你喜欢
  • 2015-04-04
  • 2012-12-11
  • 1970-01-01
  • 1970-01-01
  • 2018-05-02
  • 2013-11-06
  • 2015-12-13
  • 2021-05-02
  • 2013-08-01
相关资源
最近更新 更多