123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- require 'rails_helper'
- require 'models/application_model_examples'
- require 'models/concerns/can_be_imported_examples'
- require 'models/concerns/has_groups_examples'
- require 'models/concerns/has_collection_update_examples'
- require 'models/concerns/has_xss_sanitized_note_examples'
- RSpec.describe Role do
- subject(:role) { create(:role) }
- it_behaves_like 'ApplicationModel'
- it_behaves_like 'CanBeImported'
- it_behaves_like 'HasGroups', group_access_factory: :role
- it_behaves_like 'HasCollectionUpdate', collection_factory: :role
- it_behaves_like 'HasXssSanitizedNote', model_factory: :role
- it_behaves_like 'Association clears cache', association: :permissions
- it_behaves_like 'Association clears cache', association: :users
- describe 'Default state' do
- describe 'of whole table:' do
- it 'has three records ("Admin", "Agent", and "Customer")' do
- expect(described_class.pluck(:name)).to match_array(%w[Admin Agent Customer])
- end
- end
- describe 'of "Admin" role:' do
- it 'has default admin permissions' do
- expect(described_class.find_by(name: 'Admin').permissions.pluck(:name))
- .to match_array(%w[admin user_preferences report knowledge_base.editor])
- end
- end
- describe 'of "Agent" role:' do
- it 'has default agent permissions' do
- expect(described_class.find_by(name: 'Agent').permissions.pluck(:name))
- .to match_array(%w[ticket.agent chat.agent cti.agent user_preferences knowledge_base.reader])
- end
- end
- describe 'of "Customer" role:' do
- it 'has default customer permissions' do
- expect(described_class.find_by(name: 'Customer').permissions.pluck(:name))
- .to match_array(
- %w[
- user_preferences.two_factor_authentication
- user_preferences.password
- user_preferences.language
- user_preferences.linked_accounts
- user_preferences.avatar
- user_preferences.appearance
- ticket.customer
- ]
- )
- end
- end
- end
- describe 'Callbacks -' do
- describe 'Permission validation:' do
- context 'with normal permission' do
- let(:permission) { create(:permission) }
- it 'can be created' do
- expect { create(:role, permissions: [permission]) }
- .to change(described_class, :count).by(1)
- end
- it 'can be added' do
- expect { role.permissions << permission }
- .to change { role.permissions.count }.by(1)
- end
- end
- context 'with disabled permission' do
- let(:permission) { create(:permission, preferences: { disabled: true }) }
- it 'cannot be created' do
- expect { create(:role, permissions: [permission]) }
- .to raise_error(%r{is disabled})
- .and not_change(described_class, :count)
- end
- it 'cannot be added' do
- expect { role.permissions << permission }
- .to raise_error(%r{is disabled})
- .and not_change { role.permissions.count }
- end
- end
- context 'with multiple, explicitly incompatible permissions' do
- let(:permission) { create(:permission, preferences: { not: [Permission.first.name] }) }
- it 'cannot be created' do
- expect { create(:role, permissions: [Permission.first, permission]) }
- .to raise_error(%r{conflicts with})
- .and not_change(described_class, :count)
- end
- it 'cannot be added' do
- role.permissions << Permission.first
- expect { role.permissions << permission }
- .to raise_error(%r{conflicts with})
- .and not_change { role.permissions.count }
- end
- end
- context 'with multiple, compatible permissions' do
- let(:permission) { create(:permission, preferences: { not: [Permission.pluck(:name).max.next] }) }
- it 'can be created' do
- expect { create(:role, permissions: [Permission.first, permission]) }
- .to change(described_class, :count).by(1)
- end
- it 'can be added' do
- role.permissions << Permission.first
- expect { role.permissions << permission }
- .to change { role.permissions.count }.by(1)
- end
- end
- end
- describe 'System-wide agent limit checks:' do
- let(:agents) { User.with_permissions('ticket.agent') }
- describe '#validate_agent_limit_by_attributes' do
- context 'when reactivating a role adds new agents' do
- subject(:role) { create(:role, :agent, active: false) }
- before { create(:user, roles: [role]) }
- context 'exceeding the system limit' do
- before { Setting.set('system_agent_limit', agents.count) }
- it 'fails and raises an error' do
- expect { role.update!(active: true) }
- .to raise_error(Exceptions::UnprocessableEntity)
- .and not_change(agents, :count)
- end
- end
- end
- end
- end
- describe 'Restrictions on #default_at_signup:' do
- context 'for roles with "admin" permissions' do
- subject(:role) { build(:role, permissions: Permission.where(name: 'admin')) }
- it 'cannot be set to true on creation' do
- role.default_at_signup = true
- expect { role.save }
- .to raise_error(Exceptions::UnprocessableEntity, %r{Cannot set default at signup})
- end
- it 'cannot be changed to true' do
- role.save
- expect { role.update(default_at_signup: true) }
- .to raise_error(Exceptions::UnprocessableEntity, %r{Cannot set default at signup})
- end
- end
- context 'for roles with permissions that are children of "admin"' do
- subject(:role) { build(:role, permissions: [permission]) }
- let(:permission) { create(:permission, name: 'admin.foo') }
- it 'cannot be set to true on creation' do
- role.default_at_signup = true
- expect { role.save }
- .to raise_error(Exceptions::UnprocessableEntity, %r{Cannot set default at signup})
- end
- it 'cannot be changed to true' do
- role.save
- expect { role.update(default_at_signup: true) }
- .to raise_error(Exceptions::UnprocessableEntity, %r{Cannot set default at signup})
- end
- end
- context 'for roles with "ticket.agent" permissions' do
- subject(:role) { build(:role, permissions: Permission.where(name: 'ticket.agent')) }
- it 'cannot be set to true on creation' do
- role.default_at_signup = true
- expect { role.save }
- .to raise_error(Exceptions::UnprocessableEntity, %r{Cannot set default at signup})
- end
- it 'cannot be changed to true' do
- role.save
- expect { role.update(default_at_signup: true) }
- .to raise_error(Exceptions::UnprocessableEntity, %r{Cannot set default at signup})
- end
- end
- end
- describe 'Cleaning up groups when ticket.agent permission is lost' do
- let(:group) { Group.first }
- it 'creates a ticket.agent role with groups' do
- role = build(:role, :agent)
- role.role_groups.build group: group, access: 'full'
- role.save!
- expect(role.groups).to contain_exactly(group)
- end
- it 'saves non-ticket.agent role without groups' do
- role = build(:role, :admin)
- role.role_groups.build group: group, access: 'full'
- role.save!
- expect(role.groups).to be_blank
- end
- end
- end
- describe '.with_permissions' do
- context 'when given a name not matching any permissions' do
- let(:permission) { 'foo' }
- let(:result) { [] }
- it 'returns an empty array' do
- expect(described_class.with_permissions(permission)).to match_array(result)
- end
- end
- context 'when given the name of a top-level permission' do
- let(:permission) { 'user_preferences' }
- let(:result) { described_class.where(name: %w[Admin Agent]) }
- it 'returns an array of roles with that permission' do
- expect(described_class.with_permissions(permission)).to match_array(result)
- end
- end
- context 'when given the name of a child permission' do
- let(:permission) { 'user_preferences.language' }
- let(:result) { described_class.all }
- it 'returns an array of roles with either that permission or an ancestor' do
- expect(described_class.with_permissions(permission)).to match_array(result)
- end
- end
- context 'when given the names of multiple permissions' do
- let(:permissions) { %w[ticket.agent ticket.customer] }
- let(:result) { described_class.where(name: %w[Agent Customer]) }
- it 'returns an array of roles matching ANY given permission' do
- expect(described_class.with_permissions(permissions)).to match_array(result)
- end
- end
- end
- describe '#with_permission?' do
- subject(:role) { described_class.find_by(name: 'Admin') }
- context 'when given the name of a permission it has' do
- it 'returns true' do
- expect(role.with_permission?('admin')).to be(true)
- end
- end
- context 'when given the name of a permission it does NOT have' do
- it 'returns false' do
- expect(role.with_permission?('ticket.customer')).to be(false)
- end
- end
- context 'when given the name of multiple permissions' do
- it 'returns true as long as ANY match' do
- expect(role.with_permission?(['admin', 'ticket.customer'])).to be(true)
- end
- end
- end
- # https://github.com/zammad/zammad/issues/5123
- describe 'Removing KB editor permission with existing KB' do
- it 'allows to remove editor but keep reader permission' do
- role = create(:role, permission_names: %w[knowledge_base.reader knowledge_base.editor])
- kb_permission = create(:knowledge_base_permission, role: role)
- role.permission_revoke('knowledge_base.editor')
- expect(kb_permission.reload).to have_attributes(access: 'reader')
- end
- end
- end
|