Posted by Stijn Pint on Jan 29, 2008
For one of our current Ruby on Rails applications, we only had a simple login system, based on the acts_as_authenticated plugin.
However, there was a growing need to have some kind of (basic) permission system.
We wanted to keep it as lightweight as possible and came up with the following solution.
This tutorial starts from an existing project that is already using the acts_as_authenticated plugin for the user accounts.
What do we want to do ?
We want to be able to grant or deny access to a “project” for a specific user.
Of course, this also means that we need an “admin”-account to manage these permissions.
1. Create a new model / controller / views / migration :
user@your-pc:~/project_path$ruby script/generate rspec_scaffold permission user_id:integer project_id:integerThis will create everything at once: the model permission.rb, the controller permissions_controller.rb, a scaffold for the view and the RSpec tests that go along with it.
Your routes.rb file also be modified with the new RESTful routing entries for the permissions-section.
Ofcourse you can choose to do everything manually or to use different type of generators, the result will be pretty much the same.
Now, modify the generated migration to look something like this:
class CreatePermissions < ActiveRecord::Migration def self.up add_column :users, :admin, :boolean, :default => 0 if admin = User.find_by_login(“admin”) admin.update_attributes(:admin => 1) else user = User.new( :login => admin, :password => yourpass, :password_confirmation => yourpass, :email => “admin@address.com” ) user.save! end create_table :permissions do |t| t.column :user_id, :integer t.column :project_id, :integer end end def self.down drop_table :permissions remove_column :users, :admin end endExecute the migration:
user@your-pc:~/project_path$ rake db:migrate2. Set up the ActiveRecord relations
class Permissions < ActiveRecord::Base belongs_to :project belongs_to :user- Prevent duplicate permissions
validates_uniqueness_of :user_id, :scope => project_id,
:message => “already has the permission for this project.”
end
- if you already have a relation “has_many :users”, you’ll have to rename this one.
has_many :users, :through => :permissions
end
- if you already have a relation “has_many :projects”, you’ll have to rename this one.
has_many :projects, :through => :permissions
end
3. Modify your controller / views.
Here I’m not going into detail, it’s just a matter of modifying the default scaffold layout according to your wishes.
The admin user will need a clean overview of the current permissions and an easy way to add and remove them.
Pretty basic stuff, but probably the part that will take the most of your time :-).
Now, let’s implement the admin restrictions.
class PermissionsController < ApplicationController before_filter :check_admin … … end class ApplicationController < ActionController::Base- acts_as_authenticated plugin
include AuthenticatedSystem
before_filter :login_required, :except => [:login]
…
def check_admin
unless admin?
flash[:error] = “You don’t have permission to access this page, please contact an Administrator.”
redirect_to :controller => ‘projects’
return
end
end
That was cool ! With a few lines of code you’re preventing non-admin users from playing around with the permission-system.
In your view, you can now make a conditional link to the permissions page thanks to the “helper_method :admin” :
Great! Next step: use our brand new permission system to prevent users from accessing projects that don’t belong to them.
4. Implementing the permission security on the projects
Your index action in the projects_controller might look something like this :
def index @projects = Project.find(:all) respond_to do |format| format.html # index.rhtml format.xml { render :xml => @projects.to_xml } end endAt http://…../projects, this gives you an overwiew of all projects in the database.
First, make sure you’ve assigned some projects to yourself in your newly designed permission interface.
Now change the first line to the following and watch what happens.
WaW! Now you only get the projects on your screen to which you, the currently logged in user, has received permission to.
As an extra safety, you could add a before_filter to your projects_controller:
class ProjectsController < ApplicationController before_filter :check_permission, :except => [:index, :new, :create] … private def check_permission begin @project = current_user.projects.find(params[:id]) rescue #record not found flash[:error] = “This project ain’t none of your business !” redirect_to :controller => ‘projects’ return end end … endI don’t think this last step is really necessary however…
If you make sure that you always use current_user.projects.find(…) instead of just Project.find() (also in your other controllers), you should be safe.
Todo
I didn’t implement the interface for the user-management yet (assigning admin rights to users), but that’s really easy from here, I’m sure you can manage that :-).
Alternative(s)
It might have been better to use a Rails plugin for this, a quick search at http://agilewebdevelopment.com/plugins shows several potential solutions. Don’t hesitate to share your experience if you ’ve used one of them !
blog comments powered by DisqusEntries per category
- 9 pages are tagged with documentum
- 12 pages are tagged with events
- 14 pages are tagged with rails
- 32 pages are tagged with ruby
- 13 pages are tagged with sharepoint
