123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- require 'rails_helper'
- RSpec.describe KnowledgeBase::PermissionsUpdate do
- describe '#update!' do
- include_context 'basic Knowledge Base'
- let(:role_editor) { create(:role, permission_names: %w[knowledge_base.editor]) }
- let(:role_another) { create(:role, permission_names: %w[knowledge_base.editor]) }
- let(:role_reader) { create(:role, permission_names: %w[knowledge_base.reader]) }
- let(:child_category) { create(:knowledge_base_category, parent: category) }
- describe 'updating itself' do
- shared_examples 'updating itself' do |object_name:|
- let(:object) { send(object_name) }
- it 'adds role permission for self' do
- described_class.new(object).update! role_editor => 'editor'
- expect(object.permissions)
- .to contain_exactly have_attributes(permissionable: object, role: role_editor, access: 'editor')
- end
- it 'adds additional role permission for self' do
- described_class.new(object).update! role_editor => 'reader'
- described_class.new(object).update! role_editor => 'reader', role_another => 'reader'
- expect(object.permissions)
- .to contain_exactly(have_attributes(role: role_editor), have_attributes(role: role_another))
- end
- it 'does not update when re-adding an existing permission' do
- described_class.new(object).update! role_editor => 'reader'
- expect { described_class.new(object).update! role_editor => 'reader' }
- .not_to change(object, :updated_at)
- end
- it 'throws error when role does not allow given access' do
- expect { described_class.new(object).update! role_reader => 'editor' }
- .to raise_error(%r{Validation failed})
- end
- end
- context 'when saving role on KB itself' do
- include_context 'updating itself', object_name: :knowledge_base
- end
- context 'when saving role on KB category' do
- include_context 'updating itself', object_name: :category
- end
- end
- describe 'updating descendants' do
- context 'when saving role on KB itself' do
- it 'adds effective permissions to descendant categories' do
- described_class.new(knowledge_base).update! role_editor => 'reader'
- expect(category.permissions_effective)
- .to contain_exactly have_attributes(role: role_editor, access: 'reader', permissionable: knowledge_base)
- end
- it 'removing permission opens up access to descendants' do
- described_class.new(knowledge_base).update! role_editor => 'editor'
- described_class.new(knowledge_base).update!(**{})
- expect(category.permissions_effective).to be_blank
- end
- context 'when category has editor role has editor role with editor permission' do
- before do
- described_class.new(category).update! role_editor => 'editor'
- category.reload
- end
- it 'removes identical permissions on descendant roles' do
- described_class.new(knowledge_base).update! role_editor => 'editor'
- category.reload
- expect(category.permissions_effective)
- .to contain_exactly have_attributes(role: role_editor, access: 'editor', permissionable: knowledge_base)
- end
- end
- context 'when child category has another role permission' do
- before do
- create(:knowledge_base_permission, permissionable: category, role: role_another, access: 'reader')
- end
- it 'removes conflicting permissions on descendant role but keeps another role' do
- described_class.new(knowledge_base).update! role_editor => 'editor'
- category.reload
- expect(category.permissions_effective)
- .to contain_exactly(
- have_attributes(role: role_editor, access: 'editor', permissionable: knowledge_base),
- have_attributes(role: role_another, access: 'reader', permissionable: category),
- )
- end
- end
- context 'when limiting sub-category in category with editor permission' do
- before do
- create(:knowledge_base_permission, permissionable: category, role: role_editor, access: 'editor')
- end
- it 'ignores redundant editor permission' do
- described_class.new(child_category).update! role_editor => 'editor'
- child_category.reload
- expect(child_category.permissions_effective)
- .to contain_exactly(
- have_attributes(role: role_editor, access: 'editor', permissionable: category),
- )
- end
- it 'raises error on updating to reader permission' do
- expect { described_class.new(child_category).update! role_editor => 'reader' }
- .to raise_error(Exceptions::UnprocessableEntity)
- end
- it 'raises error on updating to none permission' do
- expect { described_class.new(child_category).update! role_editor => 'none' }
- .to raise_error(Exceptions::UnprocessableEntity)
- end
- end
- context 'when limiting sub-category in category with none permission' do
- before do
- create(:knowledge_base_permission, permissionable: category, role: role_editor, access: 'none')
- end
- it 'ignores redundant none permission' do
- described_class.new(child_category).update! role_editor => 'none'
- child_category.reload
- expect(child_category.permissions_effective)
- .to contain_exactly(
- have_attributes(role: role_editor, access: 'none', permissionable: category),
- )
- end
- it 'raises error on updating to reader permission' do
- expect { described_class.new(child_category).update! role_editor => 'reader' }
- .to raise_error(Exceptions::UnprocessableEntity)
- end
- it 'raises error on updating to editor permission' do
- expect { described_class.new(child_category).update! role_editor => 'editor' }
- .to raise_error(Exceptions::UnprocessableEntity)
- end
- end
- end
- context 'when saving role on KB category' do
- it 'adds effective permissions to descendant roles' do
- described_class.new(category).update! role_editor => 'reader'
- expect(child_category.permissions_effective)
- .to contain_exactly have_attributes(role: role_editor, access: 'reader', permissionable: category)
- end
- context 'when child category has editor role with editor permission' do
- before do
- described_class.new(child_category).update! role_editor => 'editor'
- category.reload
- child_category.reload
- end
- it 'removes conflicting permissions on descendant roles' do
- described_class.new(category).update! role_editor => 'none'
- category.reload
- child_category.reload
- expect(child_category.permissions_effective)
- .to contain_exactly have_attributes(role: role_editor, access: 'none', permissionable: category)
- end
- it 'removes identical permissions on descendant roles' do
- described_class.new(category).update! role_editor => 'editor'
- category.reload
- child_category.reload
- expect(child_category.permissions_effective)
- .to contain_exactly have_attributes(role: role_editor, access: 'editor', permissionable: category)
- end
- context 'when child category has another role permission' do
- before do
- create(:knowledge_base_permission, permissionable: child_category, role: role_another, access: 'reader')
- end
- it 'removes conflicting permissions on descendant role but keeps another role' do
- described_class.new(category).update! role_editor => 'none'
- category.reload
- child_category.reload
- expect(child_category.permissions_effective)
- .to contain_exactly(
- have_attributes(role: role_editor, access: 'none', permissionable: category),
- have_attributes(role: role_another, access: 'reader', permissionable: child_category),
- )
- end
- end
- end
- context 'when category has role editor with none permission' do
- before do
- described_class.new(category).update! role_editor => 'none'
- category.reload
- end
- it 'removing permission opens up access to descendants' do
- described_class.new(category).update!(**{})
- category.reload
- expect(child_category.permissions_effective).to be_blank
- end
- end
- end
- end
- describe 'preventing user lockout' do
- let(:user) { create(:admin) }
- let(:role) { user.roles.first }
- shared_examples 'preventing user lockout' do |object_name:|
- let(:object) { send(object_name) }
- it 'raises an error when saving a lockout change for a given user' do
- expect { described_class.new(object, user).update! role => 'reader' }
- .to raise_error(Exceptions::UnprocessableEntity)
- end
- it 'allows to save same change without a user' do
- expect { described_class.new(object).update! role => 'reader' }.not_to raise_error
- end
- end
- context 'when saving role on KB itself' do
- include_context 'preventing user lockout', object_name: 'knowledge_base'
- end
- context 'when saving role on KB category' do
- include_context 'preventing user lockout', object_name: 'category'
- end
- end
- end
- describe '#update_using_params!' do
- subject(:updater) { described_class.new(category) }
- let(:role) { create(:role, permission_names: %w[knowledge_base.editor]) }
- let(:category) { create(:knowledge_base_category) }
- it 'calls update! with given roles' do
- updater.update_using_params!({ permissions: { role.id => 'editor' } })
- expect(category.permissions.first).to have_attributes(role: role, access: 'editor', permissionable: category)
- end
- it 'raises an error when given a non existant role' do
- expect { updater.update_using_params!({ permissions: { (role.id + 1) => 'editor' } }) }
- .to raise_error(ActiveRecord::RecordNotFound)
- end
- end
- end
|