Dan Tappin
Dan Tappin

Reputation: 3032

Can I set a nested attribute in the controller vs. the view

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

Answers (1)

sgrif
sgrif

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

Related Questions