Reputation: 127
I have a field otp_set_up, which in the company_user model is allowed to be "true" or "false".
There is a use case where a sys admin user can reset this field to "false".
While the field can be set to "true" through code, NO user can set it to "true" via a form edit etc.
I haven't added to it the validation in the model since it can be "true" or "false".
I have the following code in a params method specific to an update in the controller before the params.require .permit bit:
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...
This works. A Sys admin user can not set otp_set_up to "true".
My question is:
Is this the best and correct way to do this in Rails? It seems a bit hacky to me, going through the params hash and removing a bit.
Is there a better / cleaner way?
Upvotes: 7
Views: 8490
Reputation: 705
I have a suggestion that you set it in the params only if the user is an admin and not otherwise. I think this is a better way.
In the model, do something like this:
if user.role == 'admin'
attr_accessor #All the params
else
attr_accessor #All the other params except the one you want to
exclude
Upvotes: 0
Reputation: 2575
delete_if
cleans it up. Still a bit hacky, but slightly less so : )
params.require(:company_user).permit(:otp_setup).delete_if do |key, val|
key == 'otp_setup' && val == true
end
This leaves the original params
object intact.
There isn't a built in way to do this. It looks like there used to be but no more https://github.com/rails/strong_parameters/issues/167
delete_if
is defined on Hash
in the core library, so it is probably the best way to do it in Ruby and by extension in Rails in the absence of a built in method.
I thought it was an interesting idea, so I wrote a small gem called allowable for this type of use case. It will add a few methods to Hash
and ActionController::Parameters
: #allow
, #allow!
, #forbid
and #forbid!
You would use it like this
params.require(:company_user).permit(:otp_setup).forbid(otp_setup: [true])
# or
params.require(:company_user).permit(:otp_setup).allow(otp_setup: [false])
You can specify a single value or an array of values, and it doesn't mutate the original params
object
Upvotes: 12
Reputation: 18090
I don't really recommend messing around with the params
object in this case. I think it's best to leave that untouched for the most part to preserve what was actually requested. That way you're not left scratching your head if you need that value again somewhere downstream.
Another approach is to build the list of attributes to accept before passing into 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)
Upvotes: 0