Avoiding a mass assignment security flaw while assigning user roles

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

I know that attr_accessible should be blank in the Role model to
prevent
attackers from changing either the role type (owner,
admin, moderator, subscriber), 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 modify a role type without a having a mass
assignment
security flaw?

Also, I would appreciate an answer to this: 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?

Models:

class User < ActiveRecord::Base
attr_accessible :name, :email

has_many :accounts, through: roles
end

class Account < ActiveRecord::Base
attr_accessible :title

belongs_to :user
end

class Role < ActiveRecord::Base
attr_accessible
end

After doing my own research, it looks like I can approach this two ways:

  1. Episode 237, Railscasts, Dynamic att_accessible, overriding
    mass_assignment_authorizer
  2. I can use attr_accessible role, as: :admin

I would appreciate it if anyone can elaborate on the merits of either
approach.

On Apr 29, 9:48pm, Mohamad El-Husseini [email protected]
wrote:

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

No it wouldn’t. You can always to foo.bar = ‘baz’, whether or not the
bar attribute is accessible or not. What attr_accessible controls is
what would happen if you did

role.update_attributes(params[:role])

Fred