Dead easy rails permissions Gem Version Build Status

Installation is as easy as 1,2,3:

1.

Add to Gemfile:
gem 'granted'

2.

Add to Rakefile:
require 'granted/tasks'

3.

Create grants migration:
rake granted:create_migration
rake db:migrate

This gem lets you define arbitrary permissions on a per object level (as opposed to roles). They are implemented purely as active record associations and hence easy to understand. Check out this readme on how to grant read/write permissions on individual documents to individual users. This is a moviepilot.com project licensed MIT. And now, code:

# Let's grant a user access to a document
user.grant(:read).on(document)

# Let's revoke a user's write access to a document
user.revoke(:grant).from(document)

# We can also do it rails association style
document.read_users << user

# Let's count all documents a user has read access to
user.readable_documents.count

# Let's count all documents a user has any access to
user.all_documents.count

# List the rights a user has to a certain document
user.grants_for(document)
=> [:read, :write]

# Define the things we took for granted (scuse me) above
class Document
  include Granted::ForGranted

  # Creates associations and grant/revoke methods
  grantable :read, :write, :destroy, to: User
end

How does it work

When creating the migration with rake granted:create_migration, this gem will add a migration to your rails app that creates a grants table when you run it. This is a polymorphic model sitting between a grantee (e.g. User and a subject (e.g. Document). It has only one attribute, and that is the right that it gives the grantee to do with the subject.

What does this code do?

class Document < ActiveRecord::Base
  include Granted::ForGranted

  grantable :read, :write, to: User
end

It does that:

class Granted::WriteGrant < Granted::Grant; end
class Granted::ReadGrant < Granted::Grant; end

class Document < ActiveRecord::Base
  has_many :grants,       as: :subject, class_name: 'Granted::Grant', dependent: :destroy
  has_many :write_grants, as: :subject, class_name: 'Granted::WriteGrant'
  has_many :read_grants,  as: :subject, class_name: 'Granted::ReadGrant'

  has_many :write_users, source: :grantee, source_type: 'User', through: :write_grants
  has_many :read_users,  source: :grantee, source_type: 'User', through: :read_grants
  has_many :all_users,   source: :grantee, source_type: 'User', through: :grants, uniq: true

  attr_accessible :write_users_attributes, :read_users_attributes
  accepts_nested_attributes_for :write_users, :read_users
end

class User < ActiveRecord::Base
  has_many :grants,       as: :grantee, class_name: 'Granted::Grant', dependent: :destroy
  has_many :write_grants, as: :grantee, class_name: 'Granted::WriteGrant'
  has_many :read_grants,  as: :grantee, class_name: 'Granted::ReadGrant'

  has_many :writeable_documents, source: :subject, source_type: 'Document', through: :write_grants
  has_many :readable_documents,  source: :subject, source_type: 'Document', through: :read_grants
  has_many :all_documents,       source: :subject, source_type: 'Document', through: :grants, uniq: true

  # It does not do this yet, but hopefully soon :)
  # attr_accessible :writeable_documents_attributes, :readable_documents_attributes
  # accepts_nested_attributes_for :writeable_documents, :readable_documents
end

First it creates STI classes that inherit from Granted::Grant, one for each right you defined as grantable (e.g. ReadGrant, WriteGrant). It then creates the appropriate has_many relations to both User and Document, so that they can be connected with a Grant instance. So you have all the access control available via normal active record associations (reading and writing).

PSA: You can only grant/revoke rights via the grantee side at the moment, the other direction is not yet implemented:

document.read_users << my_user         # Works
my_user.readable_documents << document # Doesn't work yet

Granting/revoking rights

So now that you know how querying grants/rights work, you might wonder how you give or revoke certain access rights to a user and a document. Consider this familiar snippet of code:

class Document < ActiveRecord::Base
  include Granted::ForGranted

  grantable :read, :write, to: User
end

It does not only create the associations, it also creates the grant and revoke methods on User and Document. They return a convenient little object (Grant::Granter, if you're curious). You can grant/revoke access rights using Users or Documents as a starting point, it's all the same:

# Both ways to grant are identical
my_user.grant(:read).on(my_document) 
my_document.grant(:read).to(my_user)

# Both ways to revoke are identical
my_user.revoke(:read).on(my_document)
my_document.revoke(:read).from(my_user)

# Clever: even weird grammatic yields identic results
my_user.on(my_document).revoke(:read)
my_document.from(:my_user).revoke(:read)

# This is what the grant/revoke methods do:
Granted::Granter.new.grant(:read).on(my_document).to(my_user)
Granted::Granter.new.revoke(:read).on(my_document).from(my_user)

Interedasting things

You can use arrays or single objects in grantable both as access rights and grantees:

class Document < ActiveRecord::Base
  include Granted::ForGranted

  grantable :read, to: [User, Editor]

  grantable :update, :destroy, to: [Editor]
end

my_document.grant(:read, :write).to(my_user)