There's this project I've been wanting to do for some time now, but haven't been able to due to a much bigger project that keeps dragging out and taking much longer to complete that I originally hoped. I finally decided to just take a break from that other project for a while and hammer this little thing out... and, it's done(ish).

There's still some things that I'd like to add, but all the core functionality is there and working. So, I'd like to finally present you with .... CramDex.

CramDex is a flashcard studying tool. Of course you can build your own decks of cards and in the true spirit of Web 2.001, you can study other people's decks and they can study yours. Sounds pretty bland, but there's more. CramDex doesn't start out flashing all of the cards in your deck at you. It randomly picks a few, and starts you on those. It tracks your progress on each, spending less time on flashcards you know, and more on those you don't. As you prove that you know certain flashcards it starts to introduce more cards, and once you've demonstrated that you've mastered a particular card, it's "set aside" and only rarely shown again (just often enough to re-enforce that knowledge). So you're constantly focusing your efforts on learning new and difficult material, and not wasting time "studying" things you already know.

Initially, new flashcards are presented as a multiple choice question, showing you either the 'Question' side of the card and giving you several possible answers, or the 'Answer' side with several possible questions. After a few of those it starts to give you just the 'Question' side, and a field to type the answer into (spelling, punctuation and capitalization counts). The 'Question' side of the card can either be words (eg. "What's the capital of Mazatlán?"), or a picture. I've got one sample deck in there now that shows maps of US States and you need to identify the pictured state.

So, go check it out, pass the link to friends, and let me know what you think. It's still not very pretty at the moment, but I have to say, I think my design skills are starting to improve... just a little.

Comments: 0 [add comment]
  • ICandy

At my day job we have several Rails apps that we've built and continue to maintain and enhance. We often add features that are usable in all of our applications, so although they're often initially developed in one app or another, they quickly get turned into plugins that we can share across all of our apps.

My boss and fellow Rails developer Aaron Baldwin has been typically hacking on plugins that are strictly internal and wouldn't be of much use to anyone outside of our organization, whereas I've been fortunate enough to get to work on things that are more generic and thus releaseable to the broader community. So, it's particularly noteworthy that Aaron released his first public Rails plugin this week, and this is one of those plugins that, now that I've used it, I don't know how I could have ever coded Rails without it.

Here's what it's for. When writing integration tests you often test that certain elements exist on the page. The problem though, is that when one of these tests fail, all you're told is that an element of some type was expected, but none were found. Was the element really not there? Or is there a typo in your test? Or on the page? Or... what? I've found myself in this scenario desperately wishing I could just see the page that the test was testing in my browser window so I could look for myself.

Enter ICandy. ICandy is a Ruby on Rails plugin that automatically displays the page from failing integration tests in your browser window. You can also manually trigger certain pages to open in your browser, if you desire. It's been tested to work with Safari and Firefox on Mac and Firefox on Ubuntu.

Comments: 0 [add comment]

I recently added attachment_fu to a personal Rails project that was already using GateKeeper and ran into some stumbling blocks. It took me some time to dig into attachment_fu's inner workings and some experimentation to figure out the best way to get to the two plugins to play nice together, so here's some tips for anyone else who might try this themselves someday.

attachment_fu is a Ruby on Rails plugin by Rick Olson that facilitates file uploads and is the successor to Rick's acts_as_attachment plugin. GateKeeper is a Ruby on Rals plugin by yours truly that provides a natural language DSL to easily setup and manage highly complex RBAC permission rules at the model level.

I'm going to use 'Picture' as my attachment_fu model for these examples. Typically with attachment_fu, this model would probably look something like...

class Picture < ActiveRecord::Base
  belongs_to :user
  has_attachment :content_type => :image, :max_size => 50.kilobytes, :thumbnails => {:thumb => 'x42'}
end 

Now, with gatekeeper, you might expect to just add a couple simple permissions so the owner can create, update and destroy the picture and share it with other users, such as....

class Picture < ActiveRecord::Base
  belongs_to :user
  has_attachment :content_type => :image, :max_size => 50.kilobytes, :thumbnails => {:thumb => 'x42'}
  
  ## Permissions ##
  crudable_by_my_user
  readable_by_anyone
  #################
  
end 

Note that the readable_by_anyone permission assumes that you have a 'is_anyone?' User instance method that always returns true.

There are two problems with the above. First, you're going to have issues the first time you try to access the thumbnail version of the image. Second is that the above is using the default :db_file storage method which actually adds a another ActiveRecord model, and which GateKeeper will immediately lock down tight. If you're not using thumbnails or the :db_file storage method, then the above is probably all you need. Everybody else, keep reading.

First, let's address the thumbnail issue, since that's the easiest to deal with. In order to support thumbnails, attachment_fu's instructions tell you to add a 'parent_id' column to your 'pictures' table. attachment_fu then automatically provides a 'parent' association on thumbnails that point back to the original uploaded image. You're going to want thumbnails to just inherit their GateKeeper permissions from the original image, so that anyone with permission to read or delete the original image can do the same to the thumbnails. To do this, just add a 'crudable_as_my_parent' permission to Picture. (Notice that that says as_my_parent, not by_my_parent.) This will allow anyone who can read the original to also read the thumbnail, and anyone who can destroy the original to also destroy the thumbnail.

class Picture < ActiveRecord::Base
  belongs_to :user
  has_attachment :content_type => :image, :max_size => 50.kilobytes, :thumbnails => {:thumb => 'x42'}

  ## Permissions ##
  crudable_by_my_user
  readable_by_anyone
  crudable_as_my_parent
  #################
end 

Ok, that was actually pretty easy. The :db_file storage method is just a little more challenging. If you're using :filesystem or :s3 as your storage medium for uploaded files, then you should be fine since neither of these use extra ActiveRecord models to access the files, so GateKeeper won't get in the way. However, if you're using :db_file, here's what you want to do. First, create a db_file.rb file in /app/models and make it look something like this (changing all three (3) occurances of 'picture' to match whatever your attachment_fu model is).

class DbFile < ActiveRecord::Base
  has_one :picture
  crudable_as_my_picture
  crudable_by_anyone :if => lambda {|db_file| db_file.picture.nil? } #pic already destroyed
end

The first line provides an association for GateKeeper to use to get back to the picture model. The second line tells DbFile to inherit it's GateKeeper permissions from the picture provided by the association. The third line is the tricky one. When pictures are destroyed, Rails will destroy the picture first, then automatically try to destroy the associated db_file (via a ':dependent => :destroy' call). However, when it goes to delete the db_file, there won't be a picture anymore to inherit permissions from, and line two will fail. We can safely assume though that if a db_file is being destroyed and if (and only if) the picture doesn't exist, it too must have just been destroyed by the current user and the db_file should follow suit. You should never have stray :db_file objects left in your database whose associated objects are gone, so there's really no risk in allowing anyone to destroy one in such an orphaned state.

We're almost done, but not quite yet. The last problem is that the DbFile class you just defined will never get loaded by Rails. attachment_fu is going to create it's own DbFile and the permissions you setup will never get loaded or used. So, let's go back to the Picture class and add one line to the very top to force your DbFile class to load before attachment_fu tries to create it's own.

require_dependency 'db_file'
class Picture < ActiveRecord::Base
  belongs_to :user
  has_attachment :content_type => :image, :max_size => 50.kilobytes, :thumbnails => {:thumb => 'x42'}

  ## Permissions ##
  crudable_by_my_user
  readable_by_anyone
  crudable_as_my_parent
  #################
end 

And that's it. GateKeeper and attachment_fu should play nice together now, giving you file upload wonderfulness and model security peace of mind. One last thing I'd like to briefly touch on is the possibility of having more than one attachment_fu model. Let's say you're running a job site that allows users to upload a photo of themselves AND a pdf resume. So, you're going to have both a Picture and a Resume class, and DbFile is going to need associations to both. Note that I haven't actually tried this, but updating your DbFile class that you built above to the following should work just fine. (Continue the pattern for any additional attachment_fu models you want to add to your application.)

class DbFile < ActiveRecord::Base
  has_one :picture
  has_one :resume
  crudable_as_my_picture
  crudable_as_my_resume
  crudable_by_anyone :if => lambda {|db_file| db_file.picture.nil? and db_file.resume.nil? }
end
Comments: 0 [add comment]

I've noticed in my site logs people are searching the Interweb Tubes for "GateKeeper" combined with other keywords that suggest they're looking for some better examples on how to use it, so I figured maybe I'd write something up to try to help.

GateKeeper is a Ruby on Rails plugin providing a natural language DSL to manage security permissions on instances of ActiveRecord classes at the Model level. Permissions may be assigned based on the current users roles, and/or by their relationships to the object in question. GateKeeper automatically intercepts all attempts to CRUD (Create, Read, Update & Destroy) instances of ActiveRecord classes and verifies the current user has permission before allowing the operation to proceed.

The main requirement of GateKeeper is that it expects that you have a 'User' class, and that it responds to the class method 'current', as in 'User.current'. If you need to use a different class (such as 'Person' or something), that can be defined in your environment.rb, but whatever it's called, it must respond to 'current' and return an object that represents the currently logged in user. So, here's a simple example of how to set that up.

Exactly how you get your users logged in is up to you and still beyond the scope of this post, but I'd recommend a SessionsController and a User.login(username, password) method. So, we'll assume that you have that setup already and that you store the 'id' of the currently logged in user in session, such as 'session[:user_id]'.

The first thing you'll want to do is have a method in your application controller that sets up the current user for the rest of the application...

class ApplicationController < ActionController::Base
  before_filter :setup_user

  def setup_user
    User.current = User.find(session[:user_id])
  end
end

Now, we need to add 'current' and 'current=' methods to User.

class User < ActiveRecord::Base
  
  ## Class Methods ##
  class << self
    
    def current
      @CURRENT_USER || User.new(:username => 'guest')
    end

    def current=(u)
      @CURRENT_USER = u
    end

  end

end

And that's it for User.current. Now, anywhere in your application (Models, Controllers, Helpers, and Views), you can just call User.current, and get an object that represents the currently logged in user. Now we'll go into some basic uses of GateKeeper.

If users can register new accounts on your site, then you'll need to allow 'guest' users permission to create new users. Update your User class with the following (previously shown class methods left out for brevity).

class User < ActiveRecord::Base
  ## Permissions ##
  creatable_by_guest
  #################

  def is_guest?
    username == 'guest'
  end
end

However, if at any point your application tries to save the current user, it could potentially end up creating a user with the username 'guest', which you probably don't want. So, update the above with the following...

  ## Permissions ##
  creatable_by_guest :unless => lambda {|new_user| new_user.is_guest? }
  #################

The 'is_guest?' method above is an example of how to define dynamic 'user roles' for GateKeeper. When you say 'creatable_by_guest', GateKeeper expects to either find a 'is_guest?' instance method on User, or a Role named 'guest' belonging to User.current (see below). Here are some examples of other dynamic user roles that you might want to define for your users.

class User < ActiveRecord::Base
  def is_premium_member?
    premium_membership_expires_on > Time.now
  end
  
  def is_logged_in_user?
    !is_guest?
  end

  def is_popular_user?
    popularity_ratings.size > 42
  end
end

class Foo < ActiveRecord::Base
  ## Permissions ##
  creatable_by_premium_member
  updatable_by_popular_user
  readable_by_logged_in_user
  #################
end

As I stated above, GateKeeper also looks for a Role belonging to User.current with the name of the Role used in the permission. To set this up, you'll want the following...

class CreateRoles < ActiveRecord::Migration
  def self.up
    create_table :roles do |t|
      t.string :name
      t.timestamps
    end
    add_index :roles, :name
    
    create_table :roles_users, :id => false do |t|
      t.integer :user_id
      t.integer :role_id
    end
    add_index :roles_users, :user_id
  end

  def self.down
    drop_table :roles
    drop_table :roles_users
  end
end

class User < ActiveRecord::Base
  has_and_belongs_to_many :roles
end

class Role < ActiveRecord::Base
  has_and_belongs_to_many :users
end

Now, if you were to create a role named 'moderator', and assign it to a few of your users, you could give those users special permissions on certain models, such as updatable_by_moderator . Take note that the role name should be all lower case and underscored so that your permissions look nice. If you have a role named 'SpecialUser', then your permissions would need look like 'readable_by_SpecialUser' which isn't beautiful code, and if the role name has spaces in it, it won't work with GateKeeper at all. 'special_user' is a much more appropriate role name for GateKeeper's purposes.

So, there you have it. I hope that helps some people get over the initial hurdle of setting up GateKeeper. I'd love to hear from anyone using GateKeeper and hear your thoughts and questions.

Comments: 0 [add comment]

I've just released ARID 0.5. This is a minor update with some small bug fixes and optimizations.

ARID ( ActiveResourceIntegrationDsl ) is a Ruby on Rails plugin offering easy to use methods for writing integration tests on RESTful Rails apps. With ARID you write simple statements like user.builds_article(:params => {:article => {:subject => 'Hey!', :content => 'Hello World!'}}) which will GET your new_article_path, check that it has a properly formatted form with fields for all the given params, and then will POST the params to articles_path and assert the expected response. You can even pass a block to the above call to perform additional tests. And YES, ARID tests AJAX calls.

More info and installation instructions at http://arid.rubyforge.org.

Comments: 0 [add comment]

I've just release version 0.3.1 of Cross Site Sniper. This is a minor bug fix update.

Cross Site Sniper is a Ruby on Rails plugin that automatically html escapes all string and text fields of ActiveRecord objects. Data is escaped as it is retrieved from the database to protect your site from XSS (cross site scripting) attacks without modifying or corrupting your original data. Convenience methods are provided to access the unescaped data when necessary. Edit forms are prepopulated with unescaped data as well to avoid user confusion and prevent double escaping.

Documentation and install instructions can be found at http://xss.rubyforge.org.

Comments: 0 [add comment]

GateKeeper 0.2 is now available. This version traverses eagerly loaded associations and checks read permissions on each loaded object for the current user. Rdocs and install instructions available at gatekeeper.rubyforge.org.

GateKeeper is a Ruby on Rails plugin providing robust Model level permissions management, allowing you to declare access permissions for instances of ActiveRecord classes with natural language commands. For instance

  • crudable_by_admin
  • creatable_by_my_author
  • updatable_by_friends_of_my_owner :unless => :is_imaginary?
  • updatable_by_monkey_of_my_uncle :if => :has_common_ancestor?
  • readable_by_employee_of_the_month
  • and a whole lot more.
Comments: 0 [add comment]

Well, it was a little more work than I'd originally anticipated, but 5Valleys is now both my personal OpenID server and a consumer for visitors wishing to use OpenID to identify themselves when posting comments. I used the ruby-openid gem, but otherwise wrote all my own code, trying to stay as RESTful as possible (OpenID itself isn't particularly friendly to REST practices), and getting a thorough understanding of the internals before my presentation at next week's Missoula Web Discussion Group meeting. If you have an OpenID, go ahead and post a comment to try it out. Hint: if you have accounts at any of these sites, then you've got an OpenID you can (hopefully) use here, or anyplace else that proudly accepts them.

Comments: 0 [add comment]

Anyone subscribed to an RSS feed for this blog should update their feed URLs. The old ones should still be working for now, but it won't be long until I remove support for them and only support the new ones. So, consider yourselves warned.

I'm due to give a presentation on OpenID at the upcoming Missoula Web Discussion Group meeting and figured before I do a presentation on it, maybe I should actually implement it somewhere first. I've been meaning to implement OpenID on the blog for a while and figured that volunteering to do the presentation would light the proverbial fire under my you-know-what.

However, even longer than I've been wanting to implement OpenID on the blog, I've been meaning to convert it all over to a RESTful routing architecture. So, I sat down this morning with the intent to do that first, then work on the OpenID stuff. There was one unexpected hurdle though.

Writing the blog software myself was, in part, a practice Rails project that I started a long time ago, and I haven't really done much to update or improve the code since the original writing. It seems my coding practices have improved considerably since then. As soon as I opened up the first model I knew I had more to do. It was f'ugly, to put it nicely, and I wasn't about to start trying to build new functionality on top of that garbage. So, some major refactoring took place this morning before I could even start on the RESTful refactoring.

So, now it's late afternoon, I haven't refactored as much as I'd like but it's much better and the routes are now RESTful. However, I haven't even started on the OpenID stuff yet. Ugh.

But anyway, the RESTful upgrade meant that the RSS URLs needed change too. I managed to keep the old feed URLs working, but I'm not going to leave that in long. So, update your RSS client with the new addresses if you want to keep getting updates.

Comments: 0 [add comment]
Announcing GateKeeper

I'm pleased to announce the official release of my latest Rails plugin "GateKeeper". GateKeeper started life as a library in one of my big personal Rails projects where I've been experimenting with multiple methods of setting, managing and enforcing access and security permissions for specific models. This project has lots of users with different security access levels to other user's data based on their roles and relationships to each other. I think this latest experiment was a huge success, so I immediately decided to give it away to you ('cause you're all that and more).

Why GateKeeper?

In most simple and straight forward Rails projects, scoping your finds through has_many associations is probably sufficient for ensuring that users can't get at data they aren't supposed to. For example, a show action might call @current_user.notes.find(params[:id]) to ensure that the current user owns the note with the passed id, or it won't even find it.

However, things quickly get more difficult when users need limited access to other user's objects based on different roles (such as Admin, Moderator, Premium Subscriber, Employee of the Month, etc), or based on their relationship with the object's owner (such as Supervisor, Teacher, Parent, etc). You might want to allow variety of people to read certain objects based on a variety of different roles and relationships, and only allow a subset of those people to update or destroy the same objects. Trying to manage all of this can be daunting, and before long your code starts to look like a mess and it becomes easier and easier to accidentally leave a hole open that allows the wrong people to see or even modify data they're not supposed to. Adding new features that will require a new layer of permissions has now become the thing nightmares are made of and you start to wonder when in the hell Rails stopped being fun.

GateKeeper to the rescue!
  • GateKeeper allows fine grained control of CRUD permissions on individual instances of any ActiveRecord Model.
  • GateKeeper lets you define these permissions in plain English in one central location right in each model.
  • GateKeeper lets you give permissions based on the current users roles (RBAC), and/or based on the current user's relationship with the model instance in question via ActiveRecord associations.
  • GateKeeper allows permissions based on associations to be easily inherited from other classes through multiple chained associations.
  • GateKeeper strictly enforces every CRUD action that occurs on every instance of every ActiveRecord model, making sure no users ever do anything that permission hasn't been explicitly granted for.
  • GateKeeper can optionally scope all finds to only return records that the current user has 'read' permission for.
That's a lot of reading! How about some samples instead?

Sure, how about this?

class User < ActiveRecord::Base
  has_many :articles
  has_and_belongs_to_many :roles
  
  ## GateKeeper Permissions ##
  # Grouped together for easy reference later
  crudable_by_admin
  creatable_by_guest :unless => lambda {|new_user| new_user.username == 'guest'}
  updatable_by_self
  ############################
  
  #etc...
end

class Article < ActiveRecord::Base
  belongs_to :author, :class_name => 'User'
  
  ## GateKeeper Permissions ##
  crudable_by_admin
  creatable_by_my_author
  readable_by_my_author
  updatable_by_my_author :unless => :published?
  readable_by_premium_member :if => :published?
  ############################
end
Ok, I'm sold! What do I need to do to get started.

Well, first, install the plugin. script/plugin install svn://rubyforge.org/var/svn/gatekeeper/trunk

Next, by default GateKeeper assumes that you have a class named 'User' that represents individual users on your site. If not, you'll need to add the following line to your environment.rb to tell GateKeeper the name of the class you use for users. GateKeeper.user_class_name = 'Person' Through the rest of this post, mentally replace any mention of "User" or "User class" with whatever class name you set this to.

Lastly, GateKeeper requires that your User class provide a 'current' class method (eg. User.current)that returns an instance of User representing the user currently logged into the site. How you implement this is based entirely on your method of keeping track of who is logged into your site. (If you ask nice enough, I can probably be talked into doing a future post to outline how I do it.)

How can I do Role Based Access Control (RBAC) with GateKeeper?

By default GateKeeper makes a bunch of assumptions about your User class. If any of this won't work for your application, you can easily override it (details at the end of the section).

If you declare a permission such as updatable_by_admin, then whenever a user tries to update an instance of the class, GateKeeper performs a couple of checks.

First, GateKeeper checks to see if instances of your User class respond to is_admin?, and if so, calls that method on User.current. If that method returns true, the update is allowed to proceed.

If the above method returns false (or isn't defined), then GateKeeper checks to see if instances of your user class respond to roles (provided by an association like has_and_belongs_to_many :roles), and if so calls User.current.roles.find_by_name('admin') to see if your user has a role with the name defined in the permission declaration. If it does, the the update is allowed to proceed.

If both of the above two checks fail, then the update is halted and a permission error is raised.

If for any reason that won't work for you (i.e. you use some other method to check users roles), then you can simply define an instance method named has_gate_keeper_role? that takes one string argument which will be the name of the roles GateKeeper wants to know about, and will return true or false. Precisely what you do inside has_gate_keeper_role? is entirely your business.

How do permissions based on associations work?

When you declare a permission such as updatable_by_my_owner, GateKeeper treats the 'my_' prefix on the role as a flag to indicate it needs to check associations on the object in question (instead of a user's role as described above). Whenever a user tries to update an instance of the class with this permission, GateKeeper expects to find a owner method (presumably provided by an association, but not necessarily) that returns an instance of User. GateKeeper then simply compares the object returned by 'owner' with User.current and if they math, the action is allowed to proceed.

Can a model "inherit" some or all of it's permissions from some other model?

Absolutely, thanks to associations and the '_as_' preposition. Let's say you have an instance of Chair, which belongs_to :table. If you declare updatable_as_my_table in your Chair class, then anyone who has update permission of the specific table that your instance of chair belongs to can also update the chair. You may find that the permission declaration that you use most often is simply crudable_as_my_<association>, which will cause instances of the class to inherit all of their permissions from the associated object.

What if the association isn't a user?

Often times associations refer to some other class with it's own associations, and to get from your current object to an instance of User you need to travel through several associations (eg. @chair.table.room.house.owner ). GateKeeper lets you dictate how to get from the current object to an instance of User in either of two styles.

First, you can simply chain multiple 'crudable_as_*' permissions across multiple associations. In the example above we had an instance of Chair inheriting it's update permissions from the instance of Table it belonged to. If in turn Table declared updatable_as_my_room then Chair and Table would both get their update permissions from Room.

Alternatively, you can declare the entire chain directly in Chair with the '_of_' separator, and you can chain as many of them toegther as you like, so you might do something like updatable_by_owner_of_house_of_room_of_my_table as long as each class has the correct associations setup so that @chair.table.room.house.owner returns an instance of User to compare to User.current.

Additional Conditionals!

In addition to all of the above, permission declarations may also be given an ':if' or ':unless' argument that takes a string, symbol, or proc that will return a boolean, allowing you limitless flexibility in dictating when certain actions are allowed to occur.

What's this "permission scoping" thing you mentioned earlier?

By default, if any of the ActiveRecord finder methods find an object that User.current doesn't have permission to read, GateKeeper will throw a permission error. Even with permission scoping turned on, this is still true any time the finder method is expected to return an exact number of objects (eg find(1,3,5,9)).

However, any ActiveRecord finder methods that typically return arrays of arbitrary length (eg. find(:all)), GateKeeper allows you to tell it to just return those that User.current has permission to read, instead of throwing an error for those they can't. This can be enabled in one of two ways.

First, you can turn permission scoping on globally for your application by setting GateKeeper.permission_scoping_enabled = true in your environment.rb. Or, if you prefer to leave permission scoping off most of the time, you can simply pass a block to GateKeeper.with_permission_scoping and that block will be executed with permission scoping enabled just for it.

Note however, that it is still strongly advisable to limit your find in some way. Since GateKeeper still needs to iterate through each and every item returned by the finder to to check if it's readable, running GateKeeper.with_permission_scoping { Article.find(:all) } is likely going to be a very long running command if you have thousands, or even just a couple hundred, articles in your database. If this happens to you, work on making your block return fewer records in the first place, such as with extra conditions, limits, or paginating.

More information?

More information, including installation instructions, can be found in the online documentation at rubyforge. Enjoy, and please let me know how it goes. You can post questions or comments either here, or on the Ruby on Rails mailing list (be sure to include the word "GateKeeper" in the body to catch my attention.)

Comments: 0 [add comment]

I released CrossSiteSniper version 0.3 today with one small enhancement.

In v0.2, only instance methods directly associated with a column in the database had the ability to get the data without html escaping via the _without_html_escaping call. Now,thanks to the magic of method_missing, that works on all methods. So, imagine you have a method defined in your model that draws from one or more text fields in your table. Usually each of those fields will be escaped by CrossSiteSniper. But now, calling that same method with the '_without_html_escaping' suffix will temporarily turn off html_escaping just long enough to for that method to get all all it's data and return the unescaped result of whatever it does. It's not a feature most people will have a need for, but we did come across a need for it at the day job, so now you can use it too.

Comments: 0 [add comment]

Cross Site Scripting aka (XSS) is a common concern for web developers. In fact, anybody developing an interactive website, small or large, should be concerned about and guard against it.

What exactly is XSS? Simply put, it's devious people with ill intentions using your website as a host to do nasty things to the computers of other people who visit your website, and it's quite easy to create a website that just lets them do it. In fact, if you're not constantly thinking about it, you're probably guilty of leaving lots of opportunities open on you're site for the bad guys to do all kinds of bad stuff to your visitors, without you knowing anything about it. The more dynamic a site is with lots of user interaction, the more potential opportunities there are to mistakenly leave a hole open. So, what can you do?

The standard method in Rails is to wrap every piece of data that comes from a user submitted source in the h() method in the view to escape any html they may have included. Many experienced developers are have developed a good habit of always knowing when to wrap things in the h() method. But, I have a couple big problems with this.

  • First, it's very easy to forget, just once, and mistakenly leave some piece of data unprotected, and eventually a diligent hacker will find it. And that's all they need is one hole. Furthermore, there's nothing to warn you of the problem, before or after someone has taken advantage of it.
  • Next, one of the main philosophies of Ruby programming is Don't Repeat Yourself (DRY), and I'm sorry, but wrapping data in h() methods in your views and helpers over and over again is very, very repetitive. 99.999% of the time I want the data wrapped, so why can't Rails just do it for me, and let me define when the exceptions are, rather than the other way around?

This is certainly not a new topic, and there are a plethora of different attempts at solutions. Some are better than others, but none that I could find over the course of several months of looking for something that met the requirements that I thought were necessary.

First, it has to be automatic and it must work in both views and helpers. Everything that needs escaping should just be escaped and at just the right moment, without making me think about it. Next, it must be escaped on output. Some plugins that attempt to solve this problem opt to escape the data before it gets entered into the database. This has the advantage of only having the process the data once on input, rather than repeatedly every time it is output. However, I think escaping on output is much better because,

  • You're not going to inadvertently corrupt legitimate data in the database, since you're saving it exactly as submitted. So when users go to edit records, everything looks just like it did when they submitted it, instead of special characters turning into weird strings of other characters. And if you find that you want to use a more selective method of filtering a particular field, you can do so simply by changing the view and not worry about a bunch of records that already got escaped the old way. NOTE: I'm strictly taking aobut defending against XSS attacks here. You should still be using proper coding techniques to defend against SQL injection attacks, and that's well beyond the scope of this post. XSS and SQL Injection are two different things and should be dealt with separately. You should NOT be depending on one tool or technique to provide you with appropriate and adequate defense against both XSS and SQL Injection.
  • If a safer method of scrubbing data becomes available, you can instantly apply it to legacy data. With data escaped on the save, you'd need to loop through all the old records, try to unescape each one and reconstruct what was originally entered, then re-escape it with the new method. That is, unless you're comfortable with half the data in your database having been escaped with one method, and the other half with the other method? And what happens when one of those records escaped the old way gets edited and then re-escaped the new way. Ughhh.
  • What if you forget to escape something, or maybe you originally intended to do the repetitive h() method, but now that your app has been live for 8 months, you realize that was a dumb idea? Now you've potentially got a database full of unescaped or partially escaped data that you'll need to either escape on output like I recommend, or loop through every record and fix one by one (that's gonna be a very long running migration).

SafeErb is one solution that comes up a lot in blogs. But my problem with it is that it doesn't prevent me from having to wrap everything in h() methods. I just makes my application explode if I forget. That's certainly better than doing nothing, since I won't be able to forget to escape something, but it's not DRY.

Another suggestion I found was switching to Erubis as a rendering engine. The problem here is that it only provides the auto-escaping of raw data in views. So, I've got to constantly think about whether or not to put <%= or <%== depending on whether or not I'm calling a helper. Then, inside helpers I still need to remember to strategically call h(), hoping I haven't forgotten anything. In my opinion, this just makes things worse rather than better. Plus, my brief tests didn't appear to yield anything of the purported speed improvements that Erubis has over ERB. Integration tests ran just as fast.

xss-shield and xss_terminate are two more I found. In fact, xss_terminate didn't come to my attention until my contribution (below) was nearly finished. However, the problems here are that xss-shield still makes me do things manually in my custom helpers, and xss_terminate is one of those escape_on_save style solutions.

There's also a few whitelist plugins out there, but having to type whitelist() all the time is worse than h() if all I'm trying to to is prevent XSS attacks. There are certainly many more options I looked at but can't remember right now. Each one that I looked at though didn't sufficiently meet my requirements.

Lastly, when I was getting ready to submit my plugin (below) to rubyforge, I discovered the autoescape plugin. That's the closest I've seen to what I wanted, but it still had an issue that I didn't like. Since it escapes things right out of the database, edit forms pre-populate with the escaped data, rather than what the user originally entered. There are some work-arounds for this, but they require doing special stuff in your forms, and like I said, I don't want to have to think about it.

To cut an already long post short, last week at the day job I started on my own contribution to the long list of attempts to solve this problem, and now that it's done, I'm very happy with it. Sure, I'm biased, but it's the only Rails plugin I know of that meets all my requirements to adequately solve this problem. So, without further ado, I'd like to introduce Cross Site Sniper, a Rails plugin that automatically escapes every String and Text field in ActiveRecord classes, provides easy access to the unescaped data when needed and the ability to specially designate certain fields to remain unescaped. Big huge thanks to my supervisor Aaron Baldwin and World Wide IDEA for giving me the time to hammer this thing out and allowing me to release it to the world.

Not everyone will agree with my requirements. Some will still choose to escape on save and have good reasons for doing so. Other's will certainly prefer one of the above solutions, a combination of two or more, or some other solution I've overlooked, or one yet to come. And that's that great thing about open source software. None of these solutions would be possible without being able to peruse the source code of Rails, and because there's several options, you can pick the one that suits you best, or create your own. I just hope some Rails developers out there find Cross Site Sniper to be the the silver bullet they've been waiting for.

Comments: 2

ARID has a new home of it's own on RubyForge, and version 0.4 and improved documentation is now available.

ARID (ActiveResourceIntegrationDSL) is a Ruby on Rails plugin providing methods for simple and DRY integration testing of conventions-compliant RESTful Rails applications.

This version contains significant changes to how test methods are formatted and is NOT backwards compatible with tests for prior versions.

Changes

  • :expected_response option is now just :expects
  • creates_ and updates_ methods no longer go through the new and edit actions.
  • added builds_ and edits_ methods that do continue on to creates_ or updates_ if passed a :params hash.
  • renamed reads_ method to shows_ and aliased lists_ to shows_.
  • renamed deletes_ to destroys_.

Bug Fixes

  • significant updates to previously sparse documentation, now RDoc-able.

Ehancements

    added option to add HTTP headers to requests
  • added support for textarea and select fields
  • added support for nested resource paths

This version is currently being used in several production Rails 1.2.X apps and one Rails 2.0RC1 app still in development.

Comments: 0 [add comment]

I have a site with a cron that runs a Rake task every minute to do a few maintenance tasks.

A user reported a problem and I went to look at the production log file to investigate, only to discover another problem. The cron task was quickly filling up the production log file. I have the environment.rb setting to rotate the logs, so it wasn't filling the server, but also meant that when I needed to go look at the logs, I only had about an hours worth, and the problem occurred an hour and a half ago. All log entries from the time the problem occurred had already been rotated out of existence. So, here's the solution I came up with.

  • Copied the production: section of my database.yml to a cron: section. and left a comment to remind myself that both sections need to be identical.
  • Added an 'after :deploy' task to my capistrano deploy.rb that creates a symbolic link from config/environments/cron.rb to production.rb.
  • Changed "RAILS_ENV=production" to "RAILS_ENV=cron" in the crontab.

End Result: The logs from the cron rake tasks are segregated to their own log files, so that production.log only has actual user driven events. The two environments are identical due to the database.yml settings and the symoblic link, so the only actual difference is the name of the log file.

Comments: 0 [add comment]

Holy cow, I just blew nearly an entire day trying to fix one stupid little problem with FCKEditor and remote_form_for. Had a lot of trouble finding the right answer online anywhere, so now that I've got it figured out, I'm posting here so hopefully the next person won't have as much trouble as I did.

I was trying to use FCKEditor in one field on a form, and wanted the form to submit to the controller via AJAX, which would respond with an RJS call to update the page. However, I found that when I clicked "Submit", the FCK field in params was blank, BUT if I clicked submit TWICE in a row, the second one sent all the correct data. Apparently, there's a similar problem with ASP.Net and using the RequiredFieldValidator, so all my searches kept leading me to answers to THAT problem, but I ain't using ASP or RequiredFieldValidator.

Eventually, I finally stopped and actually read all of the listings on the FCKEditor Wiki Troubleshooting page and found this post, which exactly described the problem. However, I'm not a big fan of ridiculously vague code with names like MyClass and MyObject... that's just not the Rails way. So... here's my variation on a theme.

First, Add this to your application.js...

var FCKFixer = {
  fixIt: function() {
    for ( i = 0; i < parent.frames.length; ++i )
      if ( parent.frames[i].FCK )
        parent.frames[i].FCK.UpdateLinkedField();
  }
}

Then add :onclick => "FCKFixer.fixIt();" as an argument to your submit tag. So, now your entire form should look something like...

<% remote_form_for :comment, @comment, :url => post_comments_path(@post) do |f| %>
    <%= f.date_select :birthday %>
    <%= f.select :flag, [['You Rock',:rocks],["You're Great",:great],['Can I Have Your Babies?',:have_babies_request]] %>
    <%= fckeditor_textarea :comment, :body %>
    <%= submit_tag 'Submit', :onclick => "FCKFixer.fixIt();" %>
<% end %>

That's it? Yes! Really? Yes! Have a nice day and see you next time.

Comments: 1

ARID (ActiveResourceIntegrationDsl) version 0.3 is released for your testing pleasure. Yes Cliff, that's a nested acronym. You know you love it! This one isn't a major update. I added some more documentation to the README, and fixed a bug that wouldn't allow tests to access the index action on controllers. I'm using this plugin quite extensively on a project at the day job now.

On Sunday, as we were getting ready for the Euchre Tournament to start, I was chatting with Cliff. He was a little fuzzy about what ARID did, which isn't surprising. My explanations so far admittedly haven't been all that clear. But hey, it's only at version 0.3, so give me a break.

Anyway, I thought a couple WITH and WITHOUT examples might do a better job. So, how's this for explaining what ARID does for your Integration tests?

New User Signing Up
With ARID
new_session do |guest|
  guest.creates_user({:user => {:username => 'george', :password => 'curious'}})
end
Without ARID
open_session do |sess| 
  sess.get new_user_path
  sess.assert_response :success
  sess.assert_select "form[action='/users'][method='post']" do
    sess.assert_select "input[name='user[username]']"
    sess.assert_select "input[name='user[password]']"
  end
  sess.post users_path, {:user => {:username => 'george', :password => 'curious'}}
  sess.assert_response :redirect
  sess.follow_redirect!
  sess.assert_response :success
end
Updating a User
With ARID
new_session_as('jonnyg','testing123') do |jon|
  jon.updates_user(42,{:user => {:username => 'newname', :password => 'newpass'}})
end
Without ARID
open_session do |sess|
  #Load Login Page and Check the Form
  sess.get new_session_path
  sess.assert_response :success
  sess.assert_select "form[action='/sessions'][method='post']" do
    sess.assert_select "input[name='user[username]']"
    sess.assert_select "input[name='user[password]']"
  end

  #Login
  sess.post sessions_path, {:user => {:username => 'jonnyg', :password => 'testing123'}}
  sess.assert_response :redirect
  sess.follow_redirect!
  sess.assert_response :success

  #Get Edit User Page and Check the Form
  sess.get edit_user_path(42)
  sess.assert_response :success
  sess.assert_select "form[action='/users/42'][method='post']" do
    sess.assert_select "input[type='hidden'][name='_method'][value='put']"
    sess.assert_select "input[name='user[username]']"
    sess.assert_select "input[name='user[password]']"
  end
  
  #Update User
  sess.put user_path(42) {:user => {:username => 'newname', :password => 'newpass'}}
  sess.assert_response :redirect
  sess.follow_redirect!
  sess.assert_response :success
  
  #Log Out
  sess.delete session_path('current')
  sess.assert_resonse :redirect
  sess.follow_redirect!
  sess.assert_response :success
end

Note that these are pretty minimalistic tests. They don't check to see that the user actually got created or updated. Since your controller may, or may not, have a 1 to 1 correlation with a model in the back end, and what you actually expect to happen to that model may change from controller to controller, I'll leave it to you to check that your app works the way you expect it to. Also, note that you can pass a block to any of the provided methods to do additional assertions on the generated page. See the README for additional details on that.

Comments: 0 [add comment]

I just released version 0.2 of ARID (ActiveResourceIntegrationDsl) [formerly known as RestfulCrudIntegrationTester]. I've been working with this on three different projects for the past week and so far am very happy with it.

Besides the name change, there's a couple major enhancements.

A new sess.exercises_ method.

This allows one call that runs through all the CRUD actions of a RESTful controller, assuming the controller doesn't do anything weird. So something like

napoleon.exercises_llama({:name => 'Tina'},{:name => 'Trisha'})
is equivalent to the 0.1 method of...
napoleon.creates_lama({:name => 'Tina'}) do |page|
  @llama = page.assigns(:llama)
end     
napoleon.reads_lama(@llama)
napoleon.updates_llama(@llama,{:name => {Trisha'})
napoleon.destroys_llama(@llama)
which runs through a big chunk of a simple RESTful controller's code and views checking that forms are all setup correctly on new and edit pages and that the controller properly processes the submitted forms and respond with :success on reads and :redirect everything else.

It now handles AJAX calls.

Simply add {:via_ajax => true} to the options hash for the lower level goes_to (GET), posts_to and puts_to functions, and it'll pass it to your controller as an XML_HTTP_REQUEST, instead of a standard HTTP. It'll assert a :success, but It's up to you to check for anything else in the response. i.e.

napoleon.puts_to(llama_path(@llama),{:name => 'Pedro'},{:via_ajax => true}) do |page|
  assert_equal('Pedro',page.assigns(:llama).name)
  page.assert_select_rjs :replace_html, :llama
end 

Comments: 0 [add comment]

When working with Rails full time, it's easy to grow accustomed to the fact that when you do things right, other things "just work" without you doing anything else.

Case in point, I just got ready to see what I needed to do to make my RestfulCrudIntegrationTester work with nested routes. First thing to do was to write an Integration test using the plugin that tried to go to a nested route and see what errors I got. As I was half expecting, I didn't get any errors. I checked the test log, and sure enough, the controller reported that it got all the expected parameters and returned the correct page. So, here's what the routes and test look like, as a demo.

map.resources :posts do |users|
  users.resources :comments, :name_prefix => 'post_'
end
@post = Post.find(3)
new_session_as('me','super_duper_pass') do |me|
  me.reads_post_comments(@post)
end

And that's it. The resulting param hash at the comments controller is {"action"=>"index", "controller"=>"comments", "post_id"=>"3"} showing that the GET request went to '/posts/3/comments' as expected.

Tangent Alert

One note, you'll see that I used the :name_prefix option on the route above. My boss and I went back and forth on this for a while this week.

He was in favor of using it, as it makes your helper methods a little more descriptive (or so he claimed). With it, instead of comments_path(@post), you say post_comments_path(@post). My argument was that it's just as clear what comments_path(@post) does once you get used to it, it's less typing, and its convention over configuration (where adding :name_prefix to routes.rb would be extra configuration, overriding convention, and counter to the Rails philosophy). If we've learned anything this past year, it's that whenever you find yourself fighting the framework, you're doing something wrong.

This went on for a while, each of us steadily getting frustrated with the other's obstinance, and since we both work on the same code, it was imperative that we reach a consensus. "Agree to Disagree" was not an available option. One of was going to have to give in.

Then, out of the blue, he pulled a trump card out of Google. In Rails 2.0, adding the prefix onto the helper methods would be default behavior, and if you didn't want them, you'd need to add :name_prefix => nil in your routes to turn it off. Well, clearly, removing a few :name_prefix => 'something_' entries from the routes after upgrading would be a lot easier than searching through the code trying to find all the references to the helper methods and fixing those. So, adding :name_prefix now is just temporary configuration, in preparation for future convention.

In the end, I think we both won. He got his name prefixes, and I got my convention. And the point the story? Start using :name_prefix in your nested routes now, and save yourself some aspirin down the road.

Comments: 0 [add comment]

Took a little time today to give the site a makeover, and fix some small bugs. I still don't claim to have any eye for aesthetics, but I think this probably looks a lot nicer than before. Clean & simple.

I also released my first public Rails plugin this morning (with the wildly clever name of RestfulCrudIntegrationTester), a whole 36 hours sooner than I thought I would yesterday.

I've just finally started delving into Integration Testing (yah, I know, I'm a little late to the party.) I was writing tests for two different projects (one at home and one at work) and noticed even though they were very different projects, I was duplicating a lot of low level integration test code. So... I extracted it out into a plugin.

Now, on both projects, and any others I use the plugin in, I can just say things like...
new_session_as('jon','Exc3l1ent_p@s5w0rd!') do |jon|
  jon.updates_article(1,{:article => {:subject => 'New Subject'}})
end
And then the Plugin will go try to login as that user (using the sessions_controller), and exercise both the edit and update actions in the articles_controller, as well as confirm that the edit form has the correct action, method, and input elements for a RESTful update.

Note, that the plugin is only for very RESTful controllers (if you didn't get that already), and it makes a few big assumptions on how your Rails app works. I tried to make sure the assumptions followed Rails conventions as much as possible.

Some of the initial code came from Jamis Buck's post on the subject from over a year ago, (yah, yah, late to the party) so thanks to Jamis for getting me started on the right path.

Strangly, as long as Integration Testing has been around, I didn't find any existing plugins to provide this kind of basic framework. So, I'm hoping I didn't waste time reinventing the wheel, and instead have found an unfilled need and provided a worthwhile contribution to the Rails community. Time will tell on that.

Here's the complete README file from the 0.1 version of plugin.

RestfulCrudIntegrationTester
============================
Provides a framework of common integration
tests for RESTful CRUD interactions.

How To Use
==========
class ArticleTest < ActionController::IntegrationTest
  include RestfulCrudIntegrationTester
  
  def test_some_article_functionality
  	new_session_as('jonnyg','testing123') do |jon|
      params = {
      	:article => {
      		:title => 'Foo Bites Bar',
      		:content => 'Bar was bitten by Foo yesterday.' 
      	}
      }
      jon.creates_story(params)
      params = {:article => {:title => 'Bar Bitten Badly'}}
      jon.updates_story(1,params)
      jon.reads_story(1)
      jon.deletes_story(1)
    end
  end
end

Assumptions
===========
The plugin makes certain assumptions about your application.
Some of these can be bypassed or overridden if your app does
not conform.

* you have appropriate map.resources lines in your routes.rb
	for any controllers you intend to test with these methods.
* sessions_path helper method returns the path to a controller
	that controls user logins. (Provided automatically by
	Rails if you have a sessions_controller and routes.rb
	includes "map.resources :sessions".)
	@ Override by adding your own sessions_path method to
	application_helper
	
* your sessions controller accepts parameters in the form of...
	{:user => {:username => 'xxx', :password => 'yyy'}}
	@ Override by providing your own new_session_as method
		that generates an appropriate session for your app
		with the user provided.

Provided Methods
================
-Create Sessions-
-----------------
new_session:
	creates generic session (i.e. user not logged into your site).
	
	#example
	new_session do |guest|
		#guest.does_things here
	end
	
new_session_as:
	creates session as user with provided username and password.
	
	***assumes that you have a 'sessions_controller' and that it
	takes params in the form of... 
	{:user => {:username => 'xxx', :password => 'yyy'}}
	
-Do CRUD-
---------
All CRUD tasks can be passed a block to do additional assertions
sess.reads_():
	performs a GET to _path()
	asserts a :success response
	yields if you passed a block containing more assertions
	
sess.creates_():
	performs a GET to new__path
	asserts a :success response
	asserts page contains a form with a _path action
		and a 'post' method.
	asserts form contains appropriately named input elements
		for each of the  you provided.
		i.e. 'person[first_name]'
	performs a POST to _path passing 
	yields if you passed a block containing more assertions
	if you did NOT pass a block
		asserts a :redirect response
		follows redirect
		asserts a :success response
	
	
sess.updates_(,)
	performs a GET to edit__path()
	asserts a :success response
	asserts page contains a form with a _path action
		and a 'post' method.
        asserts form contains a hidden input field named '_method'
    	        and a value of 'put'.
	asserts form contains appropriately named input elements
		for each of the  you provided.
		i.e. 'person[first_name]'
	performs a PUT to _path passing 
	yields if you passed a block containing more assertions
	if you did NOT pass a block
		asserts a :redirect response
		follows redirect
		asserts a :success response
		
sess.deletes_():
	performs a DELETE to _path()
	yields if you passed a block containing more assertions
	if you did NOT pass a block
		asserts a :redirect response
		follows redirect
		asserts a :success response
Comments: 0 [add comment]

A project at the day job has me implementing some in pace editing. A quick search of the web found this page with a nice helper method that does all the hard work for me. Problem was, we're using RESTful routes and I was getting errors. Looking through the comments on the above linked blog post it was obvious I wasn't the only one having problems. Some more research and I think I've got it solved. Two options need to be added to the :ajax section of the options.

:ajax => {
  :ajaxOptions => "{ method: 'put' }",
  :paramName => "'my_model[my_field]'"
}

REST expects updates to be sent as a PUT, not a POST, which is what the browser wants to do by default, hence the necessity of the first option.

Next, paramName seems to be an undocumented option that I found only after digging through the Prototype source code. That changes the name of the text field in the form, so that it plays nicely with Rails apps. Obviousl,y you'll need to change 'my_model[my_field]' to the appropriate model and field names. Notice the nested double and single quotes... these are IMPORTANT!

With those two options added to the rest, it seems to work like a charm.

Comments: 2

Got the power bill today, and you'll never guess what happened.

Ok, maybe you will. I thought I was doing a pretty bad job at unplugging the router and stuff when not in use the past month, so I didn't think I'd beat my previous record of 114 KwH. But I guess I was doing better than I thought, 'cause the latest bill shows that I'm just 3 away from the magical double digits. That's 102 KwH for an entire month. According to some of the stuff I've read lately, that's what the average inefficient homeowner uses in just two or three days. I'm not sure what to expect from the next bill. I broke down and ran the dryer a couple times since the end of that billing period, but I was also out of town for 5 days at RailsConf, and unplugged just about every damn thing I could before I left, so... we'll have to wait and see.

Speaking of RailsConf, part of my motivation for setting up this blog was to occasionally talk about Rails, which I have yet to do, so, here goes.

Thanks to the day job for paying all the registration fees and travel expenses for RailsConf. Considering I work for a non-profit now (and my previous job at a much bigger company would have done no such thing), that's pretty fucking amazing. Since this was the first conference of this sort I've been to, I wasn't sure what to expect. I was just one among lots and lots of geeks, and that wasn't surprising. But I think I expected more of the presentations to be low level "Let's dissect some code together" kind of things, and some of them certainly were. But there were also a lot of high level theory presentations that weren't even Rails specific. So, I think that kind of disappointed me. But that just means if I go next year I'll be a more discriminating about which presentations I go to.

One of my favorite presentations though was Josh Sussers's presentation on contributing to Rails Source. I'll not bore anyone with the details, but Josh gave point by point directions to get started and made it seem very easy to take part (assuming you can code), with the hardest part being having the patience to wait for someone to review your submissions. I guess there's a backlog of hundreds of tickets and patches waiting to be reviewed. I managed to corner Josh at the final reception in the lobby and get some tips. His big suggestion was to find a submitted patch that didn't come with a test. Then, write a test case that proves the patch fixes something, and add that to the ticket. So, that night back at the hotel I figured I'd browse around the rails bug tracking site and see what I could find. Well, before I knew it, 7 minutes before midnight Pacific Time, I'd submitted my first patch to Rails Source. So, now the waiting game begins.

There was one more thing that surprised me at RailsConf... a marching band... or at least that's what they called themselves. Twasn't anything like the marching band I was in in High School.

Comments: 0 [add comment]

Note to self; To get Eclipse to recognize the Rails plugins in Feisty,..

  • Install sun-java6-jre package
  • cd /usr/lib/jvm
  • sudo mv java-gcj java-gcj.old
  • sudo ln -s java-6-sun-1.6.0.00 java-gcj

Comments: 2

Somehow I just recently managed to stumble on OpenCongress.org. It seems to have more info and better organized than VoteSmart.org (which is still a good site, don't get me wrong).

OC appears to have tons of RSS/Atom feeds available (which is how it should be), and I mean TONS!!!

For instance, there are 4178 'issue areas' defined by the 'Congressional Research Service' that get assigned to bills. Basically, they're 'tags'. Yes, that's right, the US Government is using tags. Who says politicians aren't hip? You can subscribe to the feed for any one of the tags, errr, issue areas. So, if "Gerrymandering" is a particularly important political issue to you, then it's absurdly easy to keep track of what congress is doing about it.

There are even feeds to alert you when a particular bill or congress person you're interested in gets mentioned in the news, or somebody's blog.

And there's a lot more, but the coolest thing about OpenCongress.org, is that it's powered by Rails.

So, keep track of your representativs, and don't forget to let them know how they're doing.

Comments: 0 [add comment]