I've been working on upgrading a personal project from Rails 2.2.2 to 2.3RC1. Things were working swell in development, but when I tried to push it up to production, there were strange errors. Finally got it working, and here's what I figured out...

All the errors I got were eventually linked back to Capistrano and how it fires up processes in production. The first error that you may see occurs if you have more than one version of the Rails gem installed on the server. It'll look something like rake aborted! can't activate rails(= 2.2.2), already activated rails-2.3]. To get around this I uninstalled everything but rails 2.3 on the server, but you shouldn't have to. Doing so just helped me get to the root of what the real problem was (keep reading).

The next error I got was no such file to load -- commands/process/reaper. Searching for this error I finally fell upon this Capistrano ticket which says that we need to install the irs_process_scripts plugin. This is a new plugin providing some deprecated code that Capistrano still needs to function properly. However if you're using Subversion on this project do not install that plugin yet!!!!. Keep reading.

As of this writing, the irs_process_scripts plugin will (immediately upon install) delete your script/process/ directory and re-create it with some new scripts in it. This means that if you're using Subversion, the .svn subdirectory in script/process/ gets deleted, which Subversion doesn't appreciate one iota. If you happened to already do this, your best bet is to check out a new copy of your project and copy the .svn directory from it's script/process back into your working copy.

To resolve this, I forked the irs_process_scripts plugin and made some changes so that it didn't delete the script/process directory and just overwrote the individual files in it that needed to change. I also opened a lighthouse ticket describing the problem. If by the time you read this my fix has been pulled into the official irs_process_scripts plugin, then you should use the official plugin. If not, and you're using Subversion in your project, then definitely use my fork *and* go comment on the lighthouse ticket with a "+1".

Comments: 0 [add comment]

Just a quick FYI to anyone interested. I've officially moved GateKeeper, CrossSiteSniper, and ARID all over to Github.

I finally got completely fed up with compatability issues between the versions of SVN built into eclipse and the one served on Rubyforge. Yes, I know that rubyforge has Git as an option now, but the social coding aspects of easily sharing and evolving code on Github is just too alluring to me, so that's where I'm headed.

Both GateKeeper and CrossSiteSniper have had some updates that are only on Github now, so anyone using these plugins are strongly urged to reinstall.

Comments: 0 [add comment]

I've been slowly trying out git and Github and doing some experimenting. Tonight I tested the 'branch' waters. There seems to be some confusion on some blogs regarding managing branches on Github. I think I've got it figured out (at least for my purposes), so for future reference and to maybe help out someone else, here are my notes. This post and this one have some tips that helped me a lot, but they're still a bit confusing and I'm not convinced the authors were very sure what they were doing when they wrote those. (caveat: I'm not really sure what I'm doing either.)

My goal was simply to be able to easily create a local branch, easily commit changes on the branch to Github, and easily merge the branch back into the master. I'll assume that if you want to follow along, you've already got a repository setup on github, a local copy of that repository on your machine, and that you're at the command line in the projects directory, and you're on the master branch. Ok, let's do it.

  #check that we're on the master branch
> git branch
* master

  #create a local branch named flibberty
> git branch flibberty

  #confirm new branch exists
> git branch
  flibberty
* master

  #push the new branch to github
> git push origin flibberty

At this point, if you go to your repository on Github and click 'all branches' you should see 'flibberty' in the list. (If you already had the page loaded, you'll need to reload the page first. Ok, so now lets do something with our branch.

  #switch to flibberty branch
> git checkout flibberty

  # Now make some changes to a file and then...
> git commit -a -m "test committing changes to a branch"
> git push 

  #switch back to the master
> git checkout master

  # Make a different change to a file here, and then
> git commit -a -m "test change committing back to master"
> git push

Ok, now at this point you should be able to swap back and forth between your master and flibberty branches on Github and see the different changes in master and flibberty. You can also go the the "Network" tab and see the map that shows the branch commits parallel to the master. But, wait, what if your co-worker created this branch, and all you've got locally is the master? How can you switch to the branch to make your own changes?

  # create a local branch tied to the one on Github and switch to it.
> git branch flibberty origin/flibberty
> git checkout flibberty

Now, let's imagine we're done making changes to the branch and ready to merge it back into the master.

> git checkout master
> git merge flibberty
> git push

And that's it. The key point here is getting the local branch onto Github, which despite convoluted instructions elsewhere, seems to just be one simple step... git push origin local_branch_name. Easy, huh? One last thing. We're done with the branch, so now we can safely delete it from Github and locally.

  # delete branch from Github (notice the colon!)
> git push origin :flibberty

  # delete local branch
> git branch -d flibberty

I think that command to delete the branch on Github is about is cryptic as it can get, which is my one complaint with git so far. After a couple years of working with Ruby and Rails and getting used to easy to read a lines of code like @user.subscriptions.find(:all, :conditions => {:active => true}) git has been a rude reminder that some programmers seem to go out of their way to be as opaque as possible... and the people that created git (Linus, I'm looking at you!) are apparently in that club.

Comments: 0 [add comment]

If you've got a laptop computer, it's not a bad idea to encrypt a data partition and keep all your personal data there. That way if your laptop gets lost or stolen, then the thief may have your computer, but they won't be able to steal your identity, too. And, doing so in linux is absurdly easy. Here's how I did it on Ubuntu. Also note that, if you've been here before and already have the encrypted partition, but are trying to access it from a new install of Ubuntu, you should *skip* steps 2, 4 & 6

Step 1) Use Synaptic to install the 'cryptsetup' and 'pam-mount' packages.

Step 2) Create a new primary partition that will be your encrypted partition (I recommend using gparted to do this).

Step 3) Restart your computer. This is a step I frequently forget, but is absolutely necessary for the right encryption modules to be loaded into your kernel.

Step 4) Run 'sudo cryptsetup luksFormat /dev/sdXX' where 'sdXX' is the name of the new partition you created in step 2 (eg, sda4). When asked for a passphrase, use the exact same password you use to login to the computer (you'll see why in step 9). Notice and heed the warning "This will overwrite data on /dev/sdXX irrevocably."

Step 5) Run 'sudo cryptsetup luksOpen /dev/sdXX sdXX'

Step 6) Run 'sudo mkfs.ext3 /dev/mapper/sdXX'

Step 7) Create a folder to mount your partition to. I like to use /mnt/sdXX, but you could just as easily put it right in your home directory, such as '~/encrypted'. Note the all instructions below assume you chose '/mnt/sdXX', so adjust accordingly.

Step 8) Run 'sudo mount /dev/mapper/sdXX /mnt/sdXX'

Ok, now your partition is created and mounted. But, next time (and every time) you restart your computer you'll need to run step 8 again. If you forget, you'll find that /mnt/sdXX is empty, and panic. Wouldn't it be nice if you could just mount that partition automatically whenever you log in? Well, as long as you used the same passphrase for the for the partition as your password when you log in, you can.

Step 9) As root, open /etc/security/pam_mount.conf.xml and add or uncomment the line that says '<luserconf name=".pam_mount.conf.xml" />'

Step 10) Create a file named .pam_mount.conf.xml in your home directory and fill it with the following...

<?xml version="1.0" encoding="utf-8" ?>

<pam_mount>

<volume fstype="crypt" path="/dev/sdXX" mountpoint="/mnt/sdXX" />

</pam_mount>

Step 11) As root, edit /etc/pam.d/gdm and add '@include common-pammount' to the end of the list.

Step 12) Reboot and you should find that the encrypted partition gets mounted for you.

One final word of warning. If an UNencrypted partition fails or gets corrupted, there are still some very crude methods to *try* and recover anything that wasn't properly backed up. When an ENcrypted partition fails like this, you can probably throw most or all of those tools and techniques out the window. So, keep your encrypted partition frequently backed up to a different *physical* drive, probably external, probably also encrypted.

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 just got back from the first Misoula BarCamp, and I think it was a success. We had about 18 people show up, which I think is very good for the first one. One attendee ( a fellow Rails developer ) came all the way from Helena. There were also some people that I was hoping/expecting would be there that weren't so hopefully they'll come to the next, whenever that is. There was a great mix of technical and non-technical discussions and presentations (I gave a little Rails demo). At any rate, big shout out and thanks to Harold for putting the whole thing together. I don't think anybody who went was the least bit disappointed in doing so, despite missing out on the first really nice spring weather in a couple weeks.

Comments: 2

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]