Simple functional authorization library and role managment for ruby. Inspired by transproc and dry project
For expample we have a simple kan class:
module Comments
module Roles
class Admin
def call(user, _)
user.admin?
end
end
end
class Abilities
include Kan::Abilities
role :admin, Roles::Admin.new
register('read') { |user, _| user&.id }
register('edit') do |user, comment|
user.id == comment.id || user.admin?
end
end
end
For testing specific ability use #ability
Abilities method:
RSpec.describe Comments::Abilities do
let(:abilities) { described_class.new }
subject { ability.call(account, nil) }
describe 'read ability' do
let(:ability) { abilities.ability(:read) } # it will return proc object
context 'when user login' do
let(:user) { User.new(id: 1) }
it { expect(subject).to eq true }
end
context 'when user anonymous' do
let(:user) { User.new(id: 1) }
it { expect(subject).to eq false }
end
end
end
Or testing specific ability using custom matchers:
RSpec.describe Comments::Abilities, type: :ability do
subject do
Kan::Application.new(
user: Users::Abilities.new,
comment: Comments::Abilities.new
)
end
describe 'read ability' do
context 'when user login' do
let(:user) { User.new(id: 1) }
it { is_expected.to permit('comment.read', user) }
end
context 'when user anonymous' do
let(:user) { User.new(id: 1) }
it { is_expected.not_to permit('comment.read', user) }
end
end
end
For testing role you can use two ways. The first - test role object:
RSpec.describe Comments::Abilities, type: :ability do
let(:role) { Comments::Role::Admin.new }
subject { role.call(user) }
context 'when user admin' do
let(:user) { User.new(admin: true) }
it { expect(subject).to be_true }
end
context 'when user anonymous' do
let(:user) { User.new(admin: false) }
it { expect(subject).to be_false }
end
end
Or use #role_block
class method:
RSpec.describe Comments::Abilities, type: :ability do
let(:role) { Comments::Abilities.role_block }
subject { role.call(user) }
context 'when user admin' do
let(:user) { User.new(admin: true) }
it { expect(subject).to be_true }
end
context 'when user anonymous' do
let(:user) { User.new(admin: false) }
it { expect(subject).to be_false }
end
end