Granity is a fine-grained authorization engine for Ruby on Rails applications. It provides a flexible DSL for defining authorization rules and efficient permission checking.
Add this gem to your application's Gemfile:
gem 'granity'
Then execute:
$ bundle install
Run the migrations:
rails granity:install:migrations
rails db:migrate
Create an initializer for Granity:
# config/initializers/granity.rb
Granity.configure do |config|
config.cache_provider = Rails.cache # Optional: Uses Rails.cache if provided
config.cache_ttl = 10.minutes
config.max_cache_size = 10_000
config.enable_tracing = !Rails.env.production?
config.max_traversal_depth = 10
end
Use the Granity DSL to define your authorization schema:
# config/initializers/granity.rb
Granity.define do
resource_type :user do
# User schema
end
resource_type :document do
relation :owner, type: :user
relation :viewer, type: :user
relation :team, type: :team
permission :view do
include_any do
include_relation :owner
include_relation :viewer
include_relation :admin from :team
end
end
permission :edit do
include_relation :owner
end
end
resource_type :team do
relation :member, type: :user
relation :admin, type: :user
end
end
# Check if a user has permission on a resource
if Granity.check_permission(
subject_type: 'user',
subject_id: current_user.id,
permission: 'view',
resource_type: 'document',
resource_id: document.id
)
# User can view the document
end
# Grant a user owner access to a document
Granity.create_relation(
object_type: 'document',
object_id: document.id,
relation: 'owner',
subject_type: 'user',
subject_id: user.id
)
# Find all users who can view a document
viewers = Granity.find_subjects(
resource_type: 'document',
resource_id: document.id,
permission: 'view'
)
class ApplicationController < ActionController::Base
def authorize!(resource, permission)
unless Granity.check_permission(
subject_type: 'user',
subject_id: current_user.id,
permission: permission,
resource_type: resource.model_name.singular,
resource_id: resource.id
)
raise Unauthorized, "Not authorized to #{permission} this #{resource.model_name.human}"
end
end
end
class DocumentsController < ApplicationController
def show
@document = Document.find(params[:id])
authorize!(@document, :view)
# ...
end
def update
@document = Document.find(params[:id])
authorize!(@document, :edit)
# ...
end
end
Define the entities in your authorization model:
resource_type :document do
# Resource definition
end
Define relationships between resources:
relation :owner, type: :user
relation :parent_folder, type: :folder
Define access rules with Boolean logic:
permission :view do
include_any do
include_relation :owner
include_relation :viewer
include_relation :editor
end
end
Combine relations with include_any
(OR) and include_all
(AND):
permission :publish do
include_all do
include_relation :editor
include_any do
include_relation :approved
include_relation :admin from :team
end
end
end
Reuse and compose permissions:
permission :manage do
include_permission :view
include_permission :edit
include_relation :owner
end
Follow paths through related resources:
include_relation :member from :team
The gem is available as open source under the terms of the MIT License.