eddorre

Buildin’ the Blog: Part 5 – Refactoring: Part 2

In my last Buildin’ the Blog article, I wrote about how I cleaned up my comments controller by moving all of the processing to the model. In this follow-up article, I’m going to write about how to access properties that aren’t part of your model object (aka table).

Take for example, a simple comments database table. It might look like this:

id full_name email_address url body

As you can see this is pretty straightforward, a single comment is comprised of a full_name, email_address, and the body. These attributes can easily be checked and processed in our comment model. Here comes the tricky part.

Take for instance my comment form below:

comment form photo

Name, email address, URL, and the body of the comment can all be mapped to database table fields. Unfortunately, Remember Me and Subscribe Via Email cannot and should not.

A beginner in Rails might write something like the following in their controller code:


if params[:subscribe]
#create a subscription record here
end

This code works but it leaves a lot of logic in the controller. The controller should be fairly lightweight, “I accept input from the user pass it to the model and then I pass it back to the browser/user.”

In order to get non-database bound fields to the model we use Ruby’s attr_accessor declaration (we could use attr_writer too to only write attributes and not read them).

For example:


class Comment < ActiveRecord::Base
  attr_accessor :subscribe
end

This allows me to create a form like the following:


  <% form_for @comment do |f| %>
  <%= f.text_field :full_name %>
  ...snip...
 <%= f.check_box :subscribe %>
 <%= f.submit %>

Once this is done, I can check what the value of the checkbox was set to in the model in a callback (let’s say either a before_save or an after_save):


  def before_save
    if self.subscribe == 1
      #create a subscription record here
    end
  end

It’s that simple. Now all of my logic can be done in the model.

With that out of the way, here is something to watch out for. Let’s say that you have a comments table that looks like this:

id full_name email_address url body is_admin

Obviously, the is_admin attribute should be set by the application and not by the user but unless you protect your model, the user can set any attribute he wants. Let’s say that you have a simple comments form with full_name, email_address, url, and body. The is_admin attribute is not displayed as a form field.

A malicious user could come by and change the form by adding the following or by submitting to the comment form using cURL. Here is an example:


  <input type="checkbox" name="comment[is_admin] checked="checked" />

By crafting this form and sending it, the user automatically becomes an admin!

One way to protect your application from this type of malicious activity is by protecting fields that should never be set by the user behind the attr_protected declaration. For example:


class Comment < ActiveRecord::Base
  attr_protected :is_admin
end

This will stop malicious users from trying to set attributes that should never be set by the user.


Comments are closed

Comments are closed on this post. If you have something on your mind regarding this post, don't hesitate to drop me a line.