Simple functional authorization library and role managment for ruby. Inspired by transproc and dry project
class Post::Abilities
include Kan::Abilities
register('read') { |_, _| true }
register('edit') { |user, post| user.id == post.user_id }
register('delete') { |_, _| false }
end
Also, you can register more than one ability in one place and use string or symbol keys:
class Post::AdminAbilities
include Kan::Abilities
register(:read, :edit, :delete) { |user, _| user.admin? }
end
class Comments::Abilities
include Kan::Abilities
register('read') { |_, _| true }
register('edit') { |user, _| user.admin? }
register(:delete) do |user, comment|
user.id == comment.user_id && comment.created_at < Time.now + TEN_MINUTES
end
end
abilities = Kan::Application.new(
post: Post::Abilities.new,
comment: Comments::Abilities.new
)
abilities['post.read'].call(current_user, post) # => true
abilities['post.delete'].call(current_user, post) # => false
abilities['comment.delete'].call(current_user, post) # => false
By default Kan use proc { true }
as a default ability block:
abilities['comment.invalid'].call(current_user, post) # => true
But you can rewrite it
admin_abilities = Kan::Application.new(
post: Post::AdminAbilities.new(default_ability_block: proc { false }),
comment: Comments::Abilities.new,
)
admin_abilities['post.delete'].call(current_user, post) # => false
admin_abilities['post.delete'].call(admin_user, post) # => true
admin_abilities['post.invalid'].call(current_user, post) # => false
You can provide array of abilities for each scope and Kan will return true
if at least one ability return true
:
global_abilities = Kan::Application.new(
post: [Post::Abilities.new, Post::AdminAbilities.new],
comment: Comments::Abilities.new
)
global_abilities['post.edit'].call(current_user, post) # => false
global_abilities['post.edit'].call(owner_user, post) # => true
global_abilities['post.edit'].call(admin_user, post) # => true
You can use strings or symbols and then use it as name of ability
class Post::Abilities
include Kan::Abilities
register(:edit) { |_, _| true }
register_alias(:correct, 'edit')
end
abilities = Kan::Application.new(
post: Post::Abilities.new
)
abilities['post.correct'].call(current_user, post) # => true
You can provide callable object (that respond to #call) that accepts ability_name and payload params to after_call_callback
param of your ability:
admin_abilities = Kan::Application.new(
post: Post::AdminAbilities.new(after_call_callback: -> (ability_name, payload) { ... }),
comment: Comments::Abilities.new,
)
admin_abilities['post.read'].call(current_user, post) # => false
Your object will be executed after calling ability.