问题
This section of Pundit section says that we could control which attributes are authorized to be updated. But it fails in case of the use of 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
If I modify the PostsController
update
action as Pundit suggested:
def update
if @post.update(permitted_attributes(@post))
render jsonapi: @post
else
render jsonapi: @post.errors, status: :unprocessable_entity
end
end
it fails with error:
ActionController::ParameterMissing (param is missing or the value is empty: post):
app/controllers/posts_controller.rb:29:in `update'
I also create the PostPolicy
as follows:
class PostPolicy < ApplicationPolicy
def permitted_attributes
if user.admin? || user.national?
[:title, :body]
else
[:body]
end
end
end
but it has no impact on the above error.
Any idea on how can we do that?
回答1:
The solution I came to (thanks to @max for some tips and tricks) is as follows:
- Add the following line to
config/application.rb
:
config.action_controller.action_on_unpermitted_parameters = :raise
- Add the
rescue_from
either to theAplicationController
or the one you are precisely interested:
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
Then add pundit_params_for
method to the PostsController
and change the update
action (in my case I'd like to restrict some attributes in update
action only:)
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
Voilà. Now if an unpermitted attribute will be submitted for the update
action, the response will have 500
status and contain the error as specified in ApplicationController#errors_response method
.
ATTENTION: It still fails if you have some relations posted with the request (for example, you can have an Author
as belongs_to relation with Post
). Using pundit_params_for
as before will fail to extract the corresponding author_id
value. To see the way, here my another post where I explained how to use it.
Hope this helps.
来源:https://stackoverflow.com/questions/58559549/rails-api-pundit-strong-parameters-with-activemodelserializers