Follow-up: 276c45b2e9 - Improved ticket policy scope handling.

This commit is contained in:
Martin Gruner 2021-10-07 16:37:57 +02:00 committed by Thorsten Eckel
parent c5ba0563a5
commit 445700d0aa
2 changed files with 107 additions and 11 deletions

View file

@ -13,7 +13,7 @@ class TicketPolicy < ApplicationPolicy
super super
end end
def resolve # rubocop:disable Metrics/AbcSize def resolve # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
raise NoMethodError, <<~ERR.chomp if instance_of?(TicketPolicy::BaseScope) raise NoMethodError, <<~ERR.chomp if instance_of?(TicketPolicy::BaseScope)
specify an access type using a subclass of TicketPolicy::BaseScope specify an access type using a subclass of TicketPolicy::BaseScope
ERR ERR
@ -26,6 +26,7 @@ class TicketPolicy < ApplicationPolicy
bind.push(user.group_ids_access(self.class::ACCESS_TYPE)) bind.push(user.group_ids_access(self.class::ACCESS_TYPE))
end end
if user.permissions?('ticket.customer')
if user.organization&.shared if user.organization&.shared
sql.push('(tickets.customer_id = ? OR tickets.organization_id = ?)') sql.push('(tickets.customer_id = ? OR tickets.organization_id = ?)')
bind.push(user.id, user.organization.id) bind.push(user.id, user.organization.id)
@ -33,6 +34,12 @@ class TicketPolicy < ApplicationPolicy
sql.push('tickets.customer_id = ?') sql.push('tickets.customer_id = ?')
bind.push(user.id) bind.push(user.id)
end end
end
# The report permission can access all tickets.
if sql.empty? && !user.permissions?('report')
sql.push '0 = 1' # Forbid unlimited access for all other permissions.
end
scope.where sql.join(' OR '), *bind scope.where sql.join(' OR '), *bind
end end

View file

@ -12,15 +12,13 @@ RSpec.shared_examples 'for agent user' do |access_type|
shared_examples 'shown' do shared_examples 'shown' do
it 'returns its groups tickets' do it 'returns its groups tickets' do
expect(scope.resolve) expect(scope.resolve).to match_array(Ticket.where(group: member_groups))
.to match_array(Ticket.where(group: member_groups))
end end
end end
shared_examples 'hidden' do shared_examples 'hidden' do
it 'does not return its groups tickets' do it 'does not return its groups tickets' do
expect(scope.resolve) expect(scope.resolve).to be_empty
.to be_empty
end end
end end
@ -47,6 +45,62 @@ RSpec.shared_examples 'for agent user' do |access_type|
end end
end end
context 'without any role permission' do
let(:role_without_rights) { create(:role) }
let(:user) { create(:agent, groups: member_groups, role_ids: [ role_without_rights.id ]) }
context 'when checking for "full" access' do
# this is already true by default, but it doesn't hurt to be explicit
before { user.user_groups.each { |ug| ug.update_columns(access: 'full') } }
include_examples 'hidden'
end
context 'when limited to "read" access' do
before { user.user_groups.each { |ug| ug.update_columns(access: 'read') } }
include_examples 'hidden'
end
context 'when limited to "overview" access' do
before { user.user_groups.each { |ug| ug.update_columns(access: 'overview') } }
include_examples 'hidden'
end
end
context 'with report permission' do
let(:report_role) { create(:role).tap { |role| role.permission_grant('report') } }
let(:user) { create(:agent, groups: member_groups, role_ids: [ report_role.id ]) }
context 'when checking for "full" access' do
# this is already true by default, but it doesn't hurt to be explicit
before { user.user_groups.each { |ug| ug.update_columns(access: 'full') } }
it 'grants access to all tickets' do
expect(scope.resolve).to match_array(Ticket.all)
end
end
context 'when limited to "read" access' do
before { user.user_groups.each { |ug| ug.update_columns(access: 'read') } }
it 'grants access to all tickets' do
expect(scope.resolve).to match_array(Ticket.all)
end
end
context 'when limited to "overview" access' do
before { user.user_groups.each { |ug| ug.update_columns(access: 'overview') } }
it 'grants access to all tickets' do
expect(scope.resolve).to match_array(Ticket.all)
end
end
end
context 'with indirect access via Role#groups' do context 'with indirect access via Role#groups' do
let(:user) { create(:agent).tap { |u| u.roles << role } } let(:user) { create(:agent).tap { |u| u.roles << role } }
let(:role) { create(:role, groups: member_groups) } let(:role) { create(:role, groups: member_groups) }
@ -70,6 +124,41 @@ RSpec.shared_examples 'for agent user' do |access_type|
include_examples access_type == 'overview' ? 'shown' : 'hidden' include_examples access_type == 'overview' ? 'shown' : 'hidden'
end end
end end
context 'when checking access via customer organization but no customer permissions' do
let(:user) { create(:agent, organization: organization) }
let(:teammate) { create(:agent, organization: organization) }
before do
create_list(:ticket, 2, customer: user)
create_list(:ticket, 2, customer: teammate)
end
context 'with no #organization' do
let(:organization) { nil }
it 'returns no tickets' do
expect(scope.resolve).to be_empty
end
end
context 'with a non-shared #organization' do
let(:organization) { create(:organization, shared: false) }
it 'returns no tickets' do
expect(scope.resolve).to be_empty
end
end
context 'with a shared #organization (default)' do
let(:organization) { create(:organization, shared: true) }
it 'returns no tickets' do
expect(scope.resolve).to be_empty
end
end
end
end end
RSpec.shared_examples 'for agent user with predefined but impossible context' do RSpec.shared_examples 'for agent user with predefined but impossible context' do