0
votes

I have User, Account, and Role models. Role stores the relationship type between Account and User.

I left attr_accessible in Role blank to prevent a mass assignment vulnerability (otherwise attackers could change the role type--owner, admin, etc...--, account or user ids).

But what if an admin wants to change a subscriber to a moderator? This would raise a mass assignment security exception:

user = User.find(params[:id])
role = user.roles.find_by_account_id(params[:account_id])
role.type = "admin"

How do I solve this? One way is to create a separate model to represent each role (owner, admin, moderator, subscriber) and use an STI type pattern. This lets me do:

user = User.find(params[:id])
user.moderatorship.build(account_id: params([:account_id])

Tedious! I would have to create Onwership, Moderatorship, Subscribership, etc..., and have them inherit from Role. If I want to stick to a single Role model, how can I have dynamic roles without exposing myself to mass assignment vulnerability?

Bonus question: Should I use a User has_many roles (user can have a single record for each role type) or has_one role (user can only have one role record, which must be toggled if their role changes) pattern?

class User < ActiveRecord::Base
  attr_accessible :name, :email
  has_many :accounts, through: roles
end

class Account < ActiveRecord::Base
  attr_accessible :title
  has_many :users, through: roles
end

class Role < ActiveRecord::Base
  attr_accessible
  belongs_to: :user
  belongs_to: :account
end
2

2 Answers

2
votes

You can use "as" with attr_accessible to have different assignment abilities. For instance,

attr_accessible :type, as: :admin

Then, when you do mass assignment, you can do something like

@role.update_attributes {type: :moderator}, as: :admin # Will update type
@role.update_attributes {type: :moderator} # Will not update type
1
votes

The most flexible approach is to override mass_assignment_authorizer method in model class to change accessible attributes dynamically.

For example:

class Article < ActiveRecord::Base
  attr_accessible :name, :content
  attr_accessor :accessible

  private
  def mass_assignment_authorizer
    super + (accessible || [])
  end
end

Now you can use it this way:

@article.accessible = [:important] if admin?

This example are from RailsCast #237, where you can learn more information about this approach.


In addition, I want to recommend you CanCan gem which can help you handle with roles and abilities.