Skip to content

Instantly share code, notes, and snippets.

@zenhob
Created March 6, 2012 22:02

Revisions

  1. zenhob revised this gist Mar 6, 2012. 1 changed file with 6 additions and 4 deletions.
    10 changes: 6 additions & 4 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -13,7 +13,8 @@ called accessible_for. They provide identical functionality.

    # Usage

    # ActiveModel-workalike API
    ## ActiveModel-workalike API

    class TacoShop < Controller
    include MassAssignmentBackport

    @@ -39,7 +40,8 @@ called accessible_for. They provide identical functionality.
    end
    end

    # accessible_for API
    ## accessible_for API

    class TacoShop < Controller
    include AccessibleFor

    @@ -79,8 +81,8 @@ Blacklisting instead of whitelisting is just a bad idea, and I see no reason
    to allow/support it when security is the primary goal.

    So once we address those two things we have something that looks a bit like
    ActiveModel's implementation minus attr_protected, but there are still two
    problems:
    ActiveModel's implementation minus attr_protected, which is the purpose of the
    ActiveModel-workalike API. However there are problems with this API as well:

    The role is optional, leading to lack of clarity. Sometimes you need to
    specify :default, sometimes it's implicit. I think an API designed for
  2. zenhob revised this gist Mar 6, 2012. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -21,10 +21,10 @@ called accessible_for. They provide identical functionality.
    attr_accessible :rating

    # you can specify multiple roles
    attr_accessible :filling, :topping, :as [:default, :manager]
    attr_accessible :filling, :topping, :as => [:default, :manager]

    # and add to existing roles
    attr_accessible :price, as: :manager
    attr_accessible :price, :as => :manager

    def update
    Taco.find(params[:id]).update_attributes!(taco_params)
  3. zenhob created this gist Mar 6, 2012.
    98 changes: 98 additions & 0 deletions gistfile1.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,98 @@
    # AccessibleFor: key-based hash sanitizer for Ruby

    This is a simple mass-assignment security module loosely based on
    [ActiveModel::MassAssignmentSecurity][1]. It attempts to steal the good ideas
    and some of the API while being compatible with Rails 2.3-based applications.

    Only attr_accessible (or its equivalent, keep reading) is implemented, because
    attr_protected is just a bad ActiveRecord API that hung around for some reason,
    and we don't want it stinking up the place.

    There are actually two available APIs, the ActiveModel-workalike and a new one
    called accessible_for. They provide identical functionality.

    # Usage

    # ActiveModel-workalike API
    class TacoShop < Controller
    include MassAssignmentBackport

    # when no role is specified, :default is used
    attr_accessible :rating

    # you can specify multiple roles
    attr_accessible :filling, :topping, :as [:default, :manager]

    # and add to existing roles
    attr_accessible :price, as: :manager

    def update
    Taco.find(params[:id]).update_attributes!(taco_params)
    end

    protected

    def taco_params
    # use sanitize_for_mass_assignment to build a safe hash given a role.
    # when nothing/nil is passed for the role, :default is used
    sanitize_for_mass_assignment params[:taco], current_user.manager? ? :manager : nil
    end
    end

    # accessible_for API
    class TacoShop < Controller
    include AccessibleFor

    # there are no implicit roles and you can declare only one group at a time
    accessible_for :default => [ :filling, :topping, :rating ]
    accessible_for :manager => [ :filling, :topping, :price ]

    def update
    Taco.find(params[:id]).update_attributes!(taco_params)
    end

    protected

    def taco_params
    # use sanitize_for(role, params) to build a safe hash
    # again, there is no implicit role
    if current_user.manager?
    sanitize_for :manager, params[:taco]
    else
    sanitize_for :default, params[:taco]
    end
    end
    end

    # Rationale

    There are two things I've never liked about ActiveRecord's attr_* API:

    It's model-level when the resources I am trying to protect are controller-level.
    This actually gets in our way when we're just trying to test/manipulate our own
    models outside of a controller context, making it harder to work with
    our own data for no good reason. I feel this phenomenon could have the effect of
    discouraging developers from using it.

    Another problem with ActiveRecord is that it provides attr_protected.
    Blacklisting instead of whitelisting is just a bad idea, and I see no reason
    to allow/support it when security is the primary goal.

    So once we address those two things we have something that looks a bit like
    ActiveModel's implementation minus attr_protected, but there are still two
    problems:

    The role is optional, leading to lack of clarity. Sometimes you need to
    specify :default, sometimes it's implicit. I think an API designed for
    hardening should be more transparent.

    The way the role is specified is also suboptimal. It's at the end of the
    declaration so you have to hunt for it. It uses the key :as implying a
    user-based access role, but the fact is this value is really just a scope and
    can mean anything.

    # Author

    Zack Hobson ([email protected])

    [1]: http://api.rubyonrails.org/classes/ActiveModel/MassAssignmentSecurity.html