Reputation: 3032
I have a simple nested form and I am setting a nested attribute with a hidden field:
<%= role_form.hidden_field :company_id, :value => session[:company_id] %>
The idea here is I am associating this nested model (a role based permissions system) to another model Company
via the company_id
set by the current session variable. My issue is that the user could send a request and create / update the role with an arbitrary company_id
and gain access to another company's account.
Can I force the nested model attributes to be this session value or perhaps a validation?
I was thinking for create:
@user = User.new(params[:user])
@user.roles.first.company_id = session[:company_id]
and for update I could do sort of the same thing.
As for the validation I tried:
accepts_nested_attributes_for :roles, :limit => 1, :allow_destroy => true , :reject_if => proc { |attributes| attributes['company_id'] != session[:company_id] }
but it looks like you can't access the session info in the model.
Any one have an idea if I can do this either of these ways?
Upvotes: 0
Views: 1016
Reputation: 3822
Rather than storing the company_id in the session, you should instead add a randomly generated token column to the company, and get the id by doing Company.find_by_token(session[:token]). If you look at how the current_user method in this Railscast on authentication, it's the same idea.
Edit: Sorry, I misunderstood your question. You should not have a hidden company_id field at all in your view. You should be setting it manually in your create method:
@user = User.new(params[:user])
@user.company_id = session[:company_id]
And you can protect the company_id from ever being set from the user changing an input name by having company_id protected against mass assignment in the model:
attr_protected :company_id
See the rails guide on mass assignment protection for more information. Note: a more common solution is something along these lines:
class ApplicationController < ActionController::Base
protect_from_forgery
def current_company
@current_company ||= Company.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token]
end
end
class User < ApplicationController
def create
@user = current_company.users.build(params[:user])
end
end
UPDATE 2:
So you're creating a user and a role, and want to do separate validation on them, this should do what you want.
role_params = params[:user].delete :role # Change to the appropriate symbol for your form
@user = User.new(params[:user])
role = @user.roles.build(role_params)
role.company_id = session[:company_id]
if(@user.save and role.user_id = @user.id and role.save) # Might want to just check for valid instead of trying to save
...
Upvotes: 1