A common requirement for Rails applications is to check permissions for certain actions on your RESTful application’s resources. There are many ways to solve this problem, ranging from simple boolean flags to full fledged role based access control. I have tried a lot of approaches and have settled on a fairly simple way that is RESTful and quite flexible.
What it does and what it does not: We are just controlling access to our app’s resources at the view and controller level, i.e. access through the web. We do not check permissions on a model or file level. That means anybody with access to the server can mess with your files, database, and models.
I consider this approach the sweet spot for most of my applications which don’t contain super sensitive information.
There are a number of places where one needs to check for permissions in a rails app:
current_user does not have permissions.One issue that comes up when checking permissions is “How do models get access to current_user?”. This is important for auditing and model security. I copied what Rails is doing: store the current user in Thread.current[:user] at the beginning of each request and wrap User.current around that variable. Then User.current is available everywhere in your app, not only in controllers and views.
The following is the recipe for implementing RESTful permissions in an Online Syllabus Builder Rails app:
There isn’t much you need on the user model. I assume you are using restful_authentication to handle users, signups, and logins. All I usually do these days is to add a boolean field for each role a user can have.
In our example the roles are ‘is_admin’ and ‘is_editor’. For each role a user holds, the corresponding field is set to ‘true’. You can then ask the user: current_user.is_admin? when checking for permissions.
Tell your model that it has permissions and customize any special permissions:
In app/models/syllabus.rb
class Syllabus < ActiveRecord::Base
has_restful_permissions
...
def updatable_by?(actor)
actor.is_a?(User) && (actor.is_admin? || self.owned_by?(actor))
end
def owned_by?(actor)
actor == self.author
end
end
The statement has_restful_permissions includes the standard set of permissions (see bottom of this post for an example). You will find that in a RESTful app, most resources have the same rules for permissions, so you can extract the permission rules into a module and include it into your resources. Then you can override individual rules for resources that are different.
The method updatable_by? overrides the default permissions for the update action on Syllabus. In this case it grants permission to update a Syllabus to logged in users (actor.is_a?(User)) who are either admins (actor.is_admin?) or who own this syllabus (self.owned_by?(actor)).
The method owned_by? defines who owns this resource. This is the method you will have to override most often as there is no general solution that fits all. In this case we consider the author of a syllabus its owner. For nested resources, you can call owned_by? on the parent resource. Example:
In app/models/text_block.rb
class TextBlock < ActiveRecord::Base
has_restful_permissions
belongs_to :syllabus
def owned_by?(actor)
self.syllabus.owned_by?(actor)
end
end
Whenever you have a resource action link that is rendered based on the current user’s permissions:
In app/views/syllabi/index.html.erb
<%= link_to('Edit', edit_syllabus_path(@syllabus)) if @syllabus.updatable_by?(current_user) %>
The ‘Edit’ link is rendered only if the current_user has permission to update this syllabus.
There is a slight edge case when rendering links for creating a new resource. Because you don’t have the resource instantiated when the view is rendered, you might have to call creatable_by? on the resource class instead of an instance. So Syllabus.creatable_by? instead of @syllabus.creatable_by?
The remaining place to check permissions is in your controller actions. I usually instantiate the resource first and then check permissions so that I can consider ownership associations as well as any other permissions that depend on the state of the resource:
In app/controllers/syllabi_controller.rb
class SyllabiController < ApplicationController
def index
# Note that this is the only case where we check permissions on the class
raise PermissionViolation unless Syllabus.listable_by?(current_user)
@syllabi = ...
...
end
def show
@syllabus = Syllabus.find(params[:id])
raise PermissionViolation unless @syllabus.updatable_by?(current_user)
...
end
def new
@syllabus = Syllabus.new
raise PermissionViolation unless @syllabus.creatable_by?(current_user)
...
end
def edit
@syllabus = Syllabus.find(params[:id])
raise PermissionViolation unless @syllabus.updatable_by?(current_user)
...
end
def create
@syllabus = Syllabus.new(params[:syllabus])
raise PermissionViolation unless @syllabus.creatable_by?(current_user)
...
end
def update
@syllabus = Syllabus.find(params[:id])
raise PermissionViolation unless @syllabus.updatable_by?(current_user)
...
end
def destroy
@syllabus = Syllabus.find(params[:id])
raise PermissionViolation unless @syllabus.destroyable_by?(current_user)
...
end
end
I first instantiate the current resource and then I ask it whether the current_user has permission to perform the requested action. This assumes that instantiating the resource has no side effects.
You can handle a PermissionViolation in the application controller.
In app/controllers/application.rb
class ApplicationController < ActionController::Base
def rescue_action(exception)
case exception
when PermissionViolation
flash[:warning] = "You do not have permission for this action."
redirect_to :back
else
super
end
end
end
This list contains the most common permissions for a RESTful resource. You might need additional permissions for each custom action method you add in routes.rb to member or collection:
@syllabus.viewable_by?(current_user)@syllabus.updatable_by?(current_user)@syllabus.creatable_by?(current_user) — your initial instinct might be to make this a class method, however I found it useful to instantiate the new object, then check permissions before I actually save it. Allows you to consider associated resources for permission checks. Sometimes you need to call creatable_by? on the class. For example when rendering a ‘new’ link in a view that allows you to create a new resource, however you don’t have one instantiated at that point to check permissions on.@syllabus.destroyable_by?(current_user)Syllabus.listable_by?(current_user) — for list I use a class method because there is no single resource for lists.So what do these methods look like? Below are some example methods that you would define in your resource class:
Anybody can list syllabi:
def self.listable_by?(actor) true end
Logged in users can view this syllabus:
def viewable_by?(actor) actor.is_a?(User) end
Admins and owners can update this post:
def updatable_by?(actor) actor.is_a?(User) && (actor.is_admin? || self.owned_by?(actor)) end
Nobody can destroy this post:
def destroyable_by?(actor) false end
I found an article by Nick Kallen that proposed a very similar approach a while ago.
He uses active voice: current_user.can_create(@syllabus). That works if you have an AnonymousUser object. And you also need to do the double dispatch where the user object then asks the resource whether it has permission. I find using the passive voice directly more concise — even though in general I prefer active voice. It is much less ambiguous.
One aspect Nick does not mention is the concept of resource ownership, handled by “owned_by?(actor)” in this example.
Also Hobo and spot.us, written by Hashrocket use a similar approach.
This recipe integrates very nicely with the make_resourceful plugin. All you need to do is call
raise PermissionViolation unless current_object.viewable_by?(current_user)
in the before_show callback. And then the resources take care of answering that question. I actually modified the plugin and added permissions checks in /vendor/plugins/make_resourceful/lib/resourceful/default/actions.rb:
module Actions
# GET /foos
def index
#load_objects
raise PermissionViolation unless current_model.listable_by?(User.current)
before :index
response_for :index
end
...
end
Something to keep in mind when working with permissions is what is called the “confused deputy problem“. Where the app intends to give a particular permission to an actor. The actor then does something either inadvertently or on purpose that goes beyond the permission granted. This usually applies to file system resources and file permissions. However it can also happen in Rails through model associations. A user might have permission to delete resource A. Resource A has_many :bs, dependent => :destroy. When the user deletes resource A, Rails automatically also deletes resource B — which the user might not have permission for. You would need model based permissions to prevent this from happening.
Here is the code I usually include in resources that have permissions with “has_restful_permissions” in the class definition.
Require it with
require 'has_restful_permissions'
In lib/has_restful_permissions.rb
class PermissionViolation < StandardError; end
module HasRestfulPermissions
# call this in resource class
def has_restful_permissions
extend ClassMethods
include InstanceMethods
end
module InstanceMethods
# permission rules, override these in the resource class
# Returns true if actor can create this new instance.
# I prefer the instance method over the class method
# because sometimes you need to look at the state
# of the newly instantiated object or its related objects
# to decide whether the current user is permitted to create it.
def creatable_by?(actor)
actor.is_a?(User)
end
# Returns true if actor can destroy this resource.
def destroyable_by?(actor)
actor.is_a?(User) && self.owned_by?(actor)
end
# Returns true if actor can update this resource.
def updatable_by?(actor)
actor.is_a?(User) && self.owned_by?(actor)
end
# Returns true if actor can view this resource.
def viewable_by?(actor)
actor.is_a?(User)
end
# Returns true if actor owns this resource.
# Expresses a control/ownership association.
# Expects actor to be a User (usually checked for in other
# permission methods before this method is called)
def owned_by?(actor)
# defaults to false. Override if resource has owner
false
end
end
module ClassMethods
# Returns true if actor can view a list of resources of this class.
def listable_by?(actor)
actor.is_a?(User)
end
# alternative way to check if resource is creatable by actor.
# use this class method instead of the instance method if resource
# is not instantiated at time of checking permissions
# This is useful when deciding whether to render a "new" link
# used for creating a new resource.
def creatable_by?(actor)
actor.is_a?(User)
end
end
end
ActiveRecord::Base.send :extend, HasRestfulPermissions
I like the idea and it is already making my app DRYer. However using restful_authentication, should actor.is_a?(Person) not be actor.is_a?(User)?
The latter worked for me whereas the former failed to ever give permission.
@Anthony: Yes, if you go with the standard naming for restful_authentication, then you have to use ‘User’ instead of ‘Person’.
I prefer to use Person (even with restful_authentication). It seems a bit more personal than ‘user’.
I am in the process of updating this recipe and will push it to github pretty soon. That’s where I will maintain these recipes. Makes managing changes easier.
Thanks for your interest.
In your intro you mentioned that Rails does something like this somewhere:
Thread.current[:user]
Can you point that out so I can better understand the context.
I implemented something similar (more general though), in my implementation I use the method missing to return false when the method ends with ‘?’, I think you could do that as well
@ActsAsFlinn: Rails uses Thread.current to store the current Timezone. Check out the Rails source for more info (lines 14 – getter and 37 – setter).
@emerb: Not sure how you use method_missing. As a safeguard for permission checks you haven’t implemented? You return false by default? I use method_missing as little as possible. Jay Fields has convinced me in one of his articles
@Jo Hund nice thanks!
Great recipe. I’ve started a new project this week and am setting everything up as you’ve outlined here. Very clear and concise instructions and lots to chew on.
I initially shrugged off using make_resourceful but now that I have all these “raise PermissionViolation” lines everywhere I can see how it will help keep it DRY. Works well together.
Oh, and your gist link still has Person in the code instead of User.
Thanks again for another amazing post. Keep ‘em coming!