【问题标题】:Rails API/Pundit: Strong parameters with ActiveModelSerializersRails API/Pundit:使用 ActiveModelSerializers 的强参数
【发布时间】:2019-10-25 13:32:02
【问题描述】:

This Pundit 部分的部分说我们可以控制哪些属性被授权更新。但是在使用active_model_seriallizers gem 的情况下会失败:

def post_params
  # originally geneated by scaffold
  #params.require(:post).permit(:title, :body, :user_id)

  #To deserialize with active_model_serializers
  ActiveModelSerializers::Deserialization.jsonapi_parse!(
        params,
        only: [:title, :body, :user]
      )
end

如果我按照 Pundit 的建议修改 PostsController update 操作:

def update
  if @post.update(permitted_attributes(@post))
    render jsonapi: @post
  else
    render jsonapi: @post.errors, status: :unprocessable_entity
  end
end

失败并出现错误:

ActionController::ParameterMissing (param is missing or the value is empty: post):
app/controllers/posts_controller.rb:29:in `update'

我还创建了PostPolicy,如下所示:

class PostPolicy < ApplicationPolicy
  def permitted_attributes
    if user.admin? || user.national?
      [:title, :body]
    else
      [:body]
    end
  end
end

但对上述错误没有影响。

关于我们如何做到这一点的任何想法?

【问题讨论】:

  • ActionController::ParameterMissing 是由ActionController::Parameters.html#require 提出的,所以你可能看错了罪魁祸首。
  • 我离解决方案越来越近了。我将pundit_params_for 添加到PostsController 如下:def pundit_params_for(_record) params.fetch(:data, {}).fetch(:attributes, {}) end,并修改update 操作如下:def update if @post.update(permitted_attributes(@post)) render jsonapi: @post else render jsonapi: @post.errors, status: :unprocessable_entity end end 。现在,如果用户无权更新title,我会在控制台中看到:Unpermitted parameter: :title
  • 很好,但我会考虑使用 require(:data).require(:attributes) 而不是 fetch。如果输入与规范不匹配,您想提前放弃,因为没有继续的意义。
  • 您的意思是 fail early 而不是 bail early 吗?
  • 是的,你说的是番茄……

标签: ruby-on-rails rails-api pundit


【解决方案1】:

我得到的解决方案(感谢@max的一些提示和技巧)如下:

  1. 将以下行添加到config/application.rb
config.action_controller.action_on_unpermitted_parameters = :raise
  1. rescue_from 添加到AplicationController 或您真正感兴趣的那个:
class ApplicationController < ActionController::API
  include ActionController::MimeResponds
  include Pundit

  rescue_from Pundit::NotAuthorizedError, ActionController::UnpermittedParameters, with: :user_not_authorized

...
private

def user_not_authorized
    render jsonapi: errors_response, status: :unathorized
  end

  def errors_response
    {
      errors:
      [
        { message: 'You are not authorized to perform this action.' }
      ]
    }
  end
end

然后将pundit_params_for 方法添加到PostsController 并更改update 操作(在我的情况下,我想限制update 操作中的某些属性:)

class PostsController < ApplicationController
...

def update
  if @post.update(permitted_attributes(@post))
    render jsonapi: @post
  else
    render jsonapi: @post.errors, status: :unprocessable_entity
  end
end

private
  def post_params
     ActiveModelSerializers::Deserialization.jsonapi_parse!(
       params,
       only: [:title, :body, :user]
     )
    end

  def pundit_params_for(_record)
    params.fetch(:data, {}).fetch(:attributes, {})
  end
end

瞧。现在,如果将针对 update 操作提交未经许可的属性,则响应将具有 500 状态并包含 ApplicationController#errors_response method 中指定的错误。

注意:如果您在请求中发布了一些关系,它仍然会失败(例如,您可以将 AuthorPost 建立为 belongs_to 关系)。像以前一样使用pundit_params_for 将无法提取相应的author_id 值。要查看方法,请查看我的 another 帖子,其中我解释了如何使用它。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-07
    • 1970-01-01
    • 2017-01-24
    相关资源
    最近更新 更多