Added general check for Group access based on agent.ticket permission.

This commit is contained in:
Thorsten Eckel 2017-06-20 17:13:42 +02:00
parent b1884c82f6
commit 18a9eed038
9 changed files with 391 additions and 259 deletions

View file

@ -7,8 +7,8 @@ module HasGroups
attr_accessor :group_access_buffer attr_accessor :group_access_buffer
after_create :check_group_access_buffer after_create :check_group_access_buffer
after_update :check_group_access_buffer after_update :check_group_access_buffer
association_attributes_ignored :groups association_attributes_ignored :groups
@ -62,6 +62,7 @@ module HasGroups
# @return [Boolean] # @return [Boolean]
def group_access?(group_id, access) def group_access?(group_id, access)
return false if !active? return false if !active?
return false if !groups_access_permission?
group_id = self.class.ensure_group_id_parameter(group_id) group_id = self.class.ensure_group_id_parameter(group_id)
access = self.class.ensure_group_access_list_parameter(access) access = self.class.ensure_group_access_list_parameter(access)
@ -95,6 +96,7 @@ module HasGroups
# @return [Array<Integer>] Group IDs the instance has the given access(es) to. # @return [Array<Integer>] Group IDs the instance has the given access(es) to.
def group_ids_access(access) def group_ids_access(access)
return [] if !active? return [] if !active?
return [] if !groups_access_permission?
access = self.class.ensure_group_access_list_parameter(access) access = self.class.ensure_group_access_list_parameter(access)
foreign_key = group_through.foreign_key foreign_key = group_through.foreign_key
@ -128,6 +130,7 @@ module HasGroups
# @return [Array<Group>] Groups the instance has the given access(es) to. # @return [Array<Group>] Groups the instance has the given access(es) to.
def groups_access(access) def groups_access(access)
return [] if !active? return [] if !active?
return [] if !groups_access_permission?
group_ids = group_ids_access(access) group_ids = group_ids_access(access)
Group.where(id: group_ids) Group.where(id: group_ids)
end end
@ -183,10 +186,24 @@ module HasGroups
@group_through ||= self.class.group_through @group_through ||= self.class.group_through
end end
# Checks if the instance has general permission to Group access.
#
# @example
# customer_user.groups_access_permission?
# #=> false
#
# @return [Boolean]
def groups_access_permission?
return true if !respond_to?(:permissions?)
permissions?('ticket.agent')
end
private private
def groups_access_map(key) def groups_access_map(key)
return {} if !active? return {} if !active?
return {} if !groups_access_permission?
{}.tap do |hash| {}.tap do |hash|
groups.access.where(active: true).pluck(key, :access).each do |entry| groups.access.where(active: true).pluck(key, :access).each do |entry|
hash[ entry[0] ] ||= [] hash[ entry[0] ] ||= []
@ -262,20 +279,7 @@ module HasGroups
# #
# @return [Array<Integer>] # @return [Array<Integer>]
def group_access_ids(group_id, access) def group_access_ids(group_id, access)
group_id = ensure_group_id_parameter(group_id) group_access(group_id, access).collect(&:id)
access = ensure_group_access_list_parameter(access)
# check direct access
ids = group_through.klass.includes(name.downcase).where(group_id: group_id, access: access, table_name => { active: true }).pluck(group_through.foreign_key)
ids ||= []
# check indirect access through roles if possible
return ids if !respond_to?(:role_access_ids)
role_instance_ids = role_access_ids(group_id, access)
# combines and removes duplicates
# and returns them in one statement
ids | role_instance_ids
end end
# Lists instances having the given access(es) to the given Group. # Lists instances having the given access(es) to the given Group.
@ -294,8 +298,22 @@ module HasGroups
# #
# @return [Array<Class>] # @return [Array<Class>]
def group_access(group_id, access) def group_access(group_id, access)
instance_ids = group_access_ids(group_id, access) group_id = ensure_group_id_parameter(group_id)
where(id: instance_ids) access = ensure_group_access_list_parameter(access)
# check direct access
ids = group_through.klass.includes(name.downcase).where(group_id: group_id, access: access, table_name => { active: true }).pluck(group_through.foreign_key)
ids ||= []
# get instances and check for required permission
instances = where(id: ids).select(&:groups_access_permission?)
# check indirect access through roles if possible
return instances if !respond_to?(:role_access)
# combines and removes duplicates
# and returns them in one statement
instances | role_access(group_id, access)
end end
# The reflection instance containing the association data # The reflection instance containing the association data

View file

@ -18,6 +18,8 @@ module HasRoles
# #
# @return [Boolean] # @return [Boolean]
def role_access?(group_id, access) def role_access?(group_id, access)
return false if !groups_access_permission?
group_id = self.class.ensure_group_id_parameter(group_id) group_id = self.class.ensure_group_id_parameter(group_id)
access = self.class.ensure_group_access_list_parameter(access) access = self.class.ensure_group_access_list_parameter(access)
@ -37,6 +39,30 @@ module HasRoles
# methods defined here are going to extend the class, not the instance of it # methods defined here are going to extend the class, not the instance of it
class_methods do class_methods do
# Lists instances having the given access(es) to the given Group through Roles.
#
# @example Group ID param
# User.role_access(1, 'read')
# #=> [1, 3, ...]
#
# @example Group param
# User.role_access(group, 'read')
# #=> [1, 3, ...]
#
# @example Access list
# User.role_access(group, ['read', 'create'])
# #=> [1, 3, ...]
#
# @return [Array<Integer>]
def role_access(group_id, access)
group_id = ensure_group_id_parameter(group_id)
access = ensure_group_access_list_parameter(access)
role_ids = RoleGroup.includes(:role).where(group_id: group_id, access: access, roles: { active: true }).pluck(:role_id)
join_table = reflect_on_association(:roles).join_table
joins(:roles).where(active: true, join_table => { role_id: role_ids }).distinct.select(&:groups_access_permission?)
end
# Lists IDs of instances having the given access(es) to the given Group through Roles. # Lists IDs of instances having the given access(es) to the given Group through Roles.
# #
# @example Group ID param # @example Group ID param
@ -53,12 +79,7 @@ module HasRoles
# #
# @return [Array<Integer>] # @return [Array<Integer>]
def role_access_ids(group_id, access) def role_access_ids(group_id, access)
group_id = ensure_group_id_parameter(group_id) role_access(group_id, access).collect(&:id)
access = ensure_group_access_list_parameter(access)
role_ids = RoleGroup.includes(:role).where(group_id: group_id, access: access, roles: { active: true }).pluck(:role_id)
join_table = reflect_on_association(:roles).join_table
includes(:roles).where(active: true, join_table => { role_id: role_ids }).distinct.pluck(:id)
end end
def ensure_group_id_parameter(group_or_id) def ensure_group_id_parameter(group_or_id)

View file

@ -768,7 +768,7 @@ perform changes on ticket
email = User.lookup(id: owner_id).email email = User.lookup(id: owner_id).email
recipients_raw.push(email) recipients_raw.push(email)
elsif recipient == 'ticket_agents' elsif recipient == 'ticket_agents'
User.group_access(group_id, 'full').order(:login).each do |user| User.group_access(group_id, 'full').sort_by(&:login).each do |user|
recipients_raw.push(user.email) recipients_raw.push(user.email)
end end
else else

View file

@ -47,10 +47,11 @@ class Transaction::Notification
recipients_and_channels = [] recipients_and_channels = []
# loop through all users # loop through all users
possible_recipients = User.group_access(ticket.group_id, 'full').order(:login) possible_recipients = User.group_access(ticket.group_id, 'full').sort_by(&:login)
if ticket.owner_id == 1 if ticket.owner_id == 1
possible_recipients.push ticket.owner possible_recipients.push ticket.owner
end end
already_checked_recipient_ids = {} already_checked_recipient_ids = {}
possible_recipients.each { |user| possible_recipients.each { |user|
result = NotificationFactory::Mailer.notification_settings(user, ticket, @item[:type]) result = NotificationFactory::Mailer.notification_settings(user, ticket, @item[:type])

View file

@ -1,10 +1,12 @@
# Requires: let(:group_access_instance) { ... }
# Requires: let(:new_group_access_instance) { ... }
RSpec.shared_examples 'HasGroups' do RSpec.shared_examples 'HasGroups' do
context 'group' do context 'group' do
let(:group_access_instance_inactive) {
let(:factory_name) { described_class.name.downcase.to_sym } group_access_instance.update_attribute(:active, false)
let(:instance) { create(factory_name) } group_access_instance
let(:instance_inactive) { create(factory_name, active: false) } }
let(:group_full) { create(:group) } let(:group_full) { create(:group) }
let(:group_read) { create(:group) } let(:group_read) { create(:group) }
let(:group_inactive) { create(:group, active: false) } let(:group_inactive) { create(:group, active: false) }
@ -20,7 +22,7 @@ RSpec.shared_examples 'HasGroups' do
end end
it 'instance responds to group_through_identifier method' do it 'instance responds to group_through_identifier method' do
expect(instance).to respond_to(described_class.group_through_identifier) expect(group_access_instance).to respond_to(described_class.group_through_identifier)
end end
end end
@ -38,19 +40,19 @@ RSpec.shared_examples 'HasGroups' do
context '#groups' do context '#groups' do
it 'responds to groups' do it 'responds to groups' do
expect(instance).to respond_to(:groups) expect(group_access_instance).to respond_to(:groups)
end end
context '#groups.access' do context '#groups.access' do
it 'responds to groups.access' do it 'responds to groups.access' do
expect(instance.groups).to respond_to(:access) expect(group_access_instance.groups).to respond_to(:access)
end end
context 'result' do context 'result' do
before(:each) do before(:each) do
instance.group_names_access_map = { group_access_instance.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => 'read', group_read.name => 'read',
group_inactive.name => 'write', group_inactive.name => 'write',
@ -58,23 +60,23 @@ RSpec.shared_examples 'HasGroups' do
end end
it 'returns all related Groups' do it 'returns all related Groups' do
expect(instance.groups.access.size).to eq(3) expect(group_access_instance.groups.access.size).to eq(3)
end end
it 'adds join table attribute(s like) access' do it 'adds join table attribute(s like) access' do
expect(instance.groups.access.first).to respond_to(:access) expect(group_access_instance.groups.access.first).to respond_to(:access)
end end
it 'filters for given access parameter' do it 'filters for given access parameter' do
expect(instance.groups.access('read')).to include(group_read) expect(group_access_instance.groups.access('read')).to include(group_read)
end end
it 'filters for given access list parameter' do it 'filters for given access list parameter' do
expect(instance.groups.access('read', 'write')).to include(group_read, group_inactive) expect(group_access_instance.groups.access('read', 'write')).to include(group_read, group_inactive)
end end
it 'always includes full access groups' do it 'always includes full access groups' do
expect(instance.groups.access('read')).to include(group_full) expect(group_access_instance.groups.access('read')).to include(group_full)
end end
end end
end end
@ -82,14 +84,14 @@ RSpec.shared_examples 'HasGroups' do
context '#group_access?' do context '#group_access?' do
before(:each) do it 'responds to group_access?' do
instance.group_names_access_map = { expect(group_access_instance).to respond_to(:group_access?)
group_read.name => 'read',
}
end end
it 'responds to group_access?' do before(:each) do
expect(instance).to respond_to(:group_access?) group_access_instance.group_names_access_map = {
group_read.name => 'read',
}
end end
context 'Group ID parameter' do context 'Group ID parameter' do
@ -105,61 +107,61 @@ RSpec.shared_examples 'HasGroups' do
end end
it 'prevents inactive Group' do it 'prevents inactive Group' do
instance.group_names_access_map = { group_access_instance.group_names_access_map = {
group_inactive.name => 'read', group_inactive.name => 'read',
} }
expect(instance.group_access?(group_inactive.id, 'read')).to be false expect(group_access_instance.group_access?(group_inactive.id, 'read')).to be false
end end
it 'prevents inactive instances' do it 'prevents inactive instances' do
instance_inactive.group_names_access_map = { group_access_instance_inactive.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
expect(instance_inactive.group_access?(group_read.id, 'read')).to be false expect(group_access_instance_inactive.group_access?(group_read.id, 'read')).to be false
end end
end end
context '#group_ids_access' do context '#group_ids_access' do
it 'responds to group_ids_access' do
expect(group_access_instance).to respond_to(:group_ids_access)
end
before(:each) do before(:each) do
instance.group_names_access_map = { group_access_instance.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
end end
it 'responds to group_ids_access' do
expect(instance).to respond_to(:group_ids_access)
end
it 'lists only active Group IDs' do it 'lists only active Group IDs' do
instance.group_names_access_map = { group_access_instance.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
group_inactive.name => 'read', group_inactive.name => 'read',
} }
result = instance.group_ids_access('read') result = group_access_instance.group_ids_access('read')
expect(result).not_to include(group_inactive.id) expect(result).not_to include(group_inactive.id)
end end
it "doesn't list for inactive instances" do it "doesn't list for inactive instances" do
instance_inactive.group_names_access_map = { group_access_instance_inactive.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
expect(instance_inactive.group_ids_access('read')).to be_empty expect(group_access_instance_inactive.group_ids_access('read')).to be_empty
end end
context 'single access' do context 'single access' do
it 'lists access Group IDs' do it 'lists access Group IDs' do
result = instance.group_ids_access('read') result = group_access_instance.group_ids_access('read')
expect(result).to include(group_read.id) expect(result).to include(group_read.id)
end end
it "doesn't list for no access" do it "doesn't list for no access" do
result = instance.group_ids_access('write') result = group_access_instance.group_ids_access('write')
expect(result).not_to include(group_read.id) expect(result).not_to include(group_read.id)
end end
end end
@ -167,12 +169,12 @@ RSpec.shared_examples 'HasGroups' do
context 'access list' do context 'access list' do
it 'lists access Group IDs' do it 'lists access Group IDs' do
result = instance.group_ids_access(%w(read write)) result = group_access_instance.group_ids_access(%w(read write))
expect(result).to include(group_read.id) expect(result).to include(group_read.id)
end end
it "doesn't list for no access" do it "doesn't list for no access" do
result = instance.group_ids_access(%w(write create)) result = group_access_instance.group_ids_access(%w(write create))
expect(result).not_to include(group_read.id) expect(result).not_to include(group_read.id)
end end
end end
@ -181,19 +183,19 @@ RSpec.shared_examples 'HasGroups' do
context '#groups_access' do context '#groups_access' do
it 'responds to groups_access' do it 'responds to groups_access' do
expect(instance).to respond_to(:groups_access) expect(group_access_instance).to respond_to(:groups_access)
end end
it 'wraps #group_ids_access' do it 'wraps #group_ids_access' do
expect(instance).to receive(:group_ids_access) expect(group_access_instance).to receive(:group_ids_access)
instance.groups_access('read') group_access_instance.groups_access('read')
end end
it 'returns Groups' do it 'returns Groups' do
instance.group_names_access_map = { group_access_instance.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
result = instance.groups_access('read') result = group_access_instance.groups_access('read')
expect(result).to include(group_read) expect(result).to include(group_read)
end end
end end
@ -201,14 +203,14 @@ RSpec.shared_examples 'HasGroups' do
context '#group_names_access_map=' do context '#group_names_access_map=' do
it 'responds to group_names_access_map=' do it 'responds to group_names_access_map=' do
expect(instance).to respond_to(:group_names_access_map=) expect(group_access_instance).to respond_to(:group_names_access_map=)
end end
context 'Group name => access relation storage' do context 'existing instance' do
it 'stores Hash with String values' do it 'stores Hash with String values' do
expect do expect do
instance.group_names_access_map = { group_access_instance.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => 'read', group_read.name => 'read',
} }
@ -217,9 +219,9 @@ RSpec.shared_examples 'HasGroups' do
}.by(2) }.by(2)
end end
it 'stores Hash with String values' do it 'stores Hash with Array<String> values' do
expect do expect do
instance.group_names_access_map = { group_access_instance.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => %w(read write), group_read.name => %w(read write),
} }
@ -227,33 +229,32 @@ RSpec.shared_examples 'HasGroups' do
described_class.group_through.klass.count described_class.group_through.klass.count
}.by(3) }.by(3)
end end
end
context 'new instance' do context 'new instance' do
let(:new_instance) { build(factory_name) }
it "doesn't store directly" do it "doesn't store directly" do
expect do expect do
new_instance.group_names_access_map = { new_group_access_instance.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => 'read', group_read.name => 'read',
}
end.not_to change {
described_class.group_through.klass.count
} }
end end.not_to change {
described_class.group_through.klass.count
}
end
it 'stores after save' do it 'stores after save' do
expect do expect do
new_instance.group_names_access_map = { new_group_access_instance.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => 'read', group_read.name => 'read',
} }
new_instance.save new_group_access_instance.save
end.to change { end.to change {
described_class.group_through.klass.count described_class.group_through.klass.count
}.by(2) }.by(2)
end
end end
end end
end end
@ -261,7 +262,7 @@ RSpec.shared_examples 'HasGroups' do
context '#group_names_access_map' do context '#group_names_access_map' do
it 'responds to group_names_access_map' do it 'responds to group_names_access_map' do
expect(instance).to respond_to(:group_names_access_map) expect(group_access_instance).to respond_to(:group_names_access_map)
end end
it 'returns instance Group name => access relations as Hash' do it 'returns instance Group name => access relations as Hash' do
@ -270,32 +271,32 @@ RSpec.shared_examples 'HasGroups' do
group_read.name => ['read'], group_read.name => ['read'],
} }
instance.group_names_access_map = expected group_access_instance.group_names_access_map = expected
expect(instance.group_names_access_map).to eq(expected) expect(group_access_instance.group_names_access_map).to eq(expected)
end end
it "doesn't map for inactive instances" do it "doesn't map for inactive instances" do
instance_inactive.group_names_access_map = { group_access_instance_inactive.group_names_access_map = {
group_full.name => ['full'], group_full.name => ['full'],
group_read.name => ['read'], group_read.name => ['read'],
} }
expect(instance_inactive.group_names_access_map).to be_empty expect(group_access_instance_inactive.group_names_access_map).to be_empty
end end
end end
context '#group_ids_access_map=' do context '#group_ids_access_map=' do
it 'responds to group_ids_access_map=' do it 'responds to group_ids_access_map=' do
expect(instance).to respond_to(:group_ids_access_map=) expect(group_access_instance).to respond_to(:group_ids_access_map=)
end end
context 'Group ID => access relation storage' do context 'existing instance' do
it 'stores Hash with String values' do it 'stores Hash with String values' do
expect do expect do
instance.group_ids_access_map = { group_access_instance.group_ids_access_map = {
group_full.id => 'full', group_full.id => 'full',
group_read.id => 'read', group_read.id => 'read',
} }
@ -306,7 +307,7 @@ RSpec.shared_examples 'HasGroups' do
it 'stores Hash with String values' do it 'stores Hash with String values' do
expect do expect do
instance.group_ids_access_map = { group_access_instance.group_ids_access_map = {
group_full.id => 'full', group_full.id => 'full',
group_read.id => %w(read write), group_read.id => %w(read write),
} }
@ -314,33 +315,32 @@ RSpec.shared_examples 'HasGroups' do
described_class.group_through.klass.count described_class.group_through.klass.count
}.by(3) }.by(3)
end end
end
context 'new instance' do context 'new instance' do
let(:new_instance) { build(factory_name) }
it "doesn't store directly" do it "doesn't store directly" do
expect do expect do
new_instance.group_ids_access_map = { new_group_access_instance.group_ids_access_map = {
group_full.id => 'full', group_full.id => 'full',
group_read.id => 'read', group_read.id => 'read',
}
end.not_to change {
described_class.group_through.klass.count
} }
end end.not_to change {
described_class.group_through.klass.count
}
end
it 'stores after save' do it 'stores after save' do
expect do expect do
new_instance.group_ids_access_map = { new_group_access_instance.group_ids_access_map = {
group_full.id => 'full', group_full.id => 'full',
group_read.id => 'read', group_read.id => 'read',
} }
new_instance.save new_group_access_instance.save
end.to change { end.to change {
described_class.group_through.klass.count described_class.group_through.klass.count
}.by(2) }.by(2)
end
end end
end end
end end
@ -348,7 +348,7 @@ RSpec.shared_examples 'HasGroups' do
context '#group_ids_access_map' do context '#group_ids_access_map' do
it 'responds to group_ids_access_map' do it 'responds to group_ids_access_map' do
expect(instance).to respond_to(:group_ids_access_map) expect(group_access_instance).to respond_to(:group_ids_access_map)
end end
it 'returns instance Group ID => access relations as Hash' do it 'returns instance Group ID => access relations as Hash' do
@ -357,18 +357,18 @@ RSpec.shared_examples 'HasGroups' do
group_read.id => ['read'], group_read.id => ['read'],
} }
instance.group_ids_access_map = expected group_access_instance.group_ids_access_map = expected
expect(instance.group_ids_access_map).to eq(expected) expect(group_access_instance.group_ids_access_map).to eq(expected)
end end
it "doesn't map for inactive instances" do it "doesn't map for inactive instances" do
instance_inactive.group_ids_access_map = { group_access_instance_inactive.group_ids_access_map = {
group_full.id => ['full'], group_full.id => ['full'],
group_read.id => ['read'], group_read.id => ['read'],
} }
expect(instance_inactive.group_ids_access_map).to be_empty expect(group_access_instance_inactive.group_ids_access_map).to be_empty
end end
end end
@ -380,8 +380,8 @@ RSpec.shared_examples 'HasGroups' do
group_read.id => ['read'], group_read.id => ['read'],
} }
instance.associations_from_param(group_ids: expected) group_access_instance.associations_from_param(group_ids: expected)
expect(instance.group_ids_access_map).to eq(expected) expect(group_access_instance.group_ids_access_map).to eq(expected)
end end
it 'handles groups parameter as group_names_access_map' do it 'handles groups parameter as group_names_access_map' do
@ -390,8 +390,8 @@ RSpec.shared_examples 'HasGroups' do
group_read.name => ['read'], group_read.name => ['read'],
} }
instance.associations_from_param(groups: expected) group_access_instance.associations_from_param(groups: expected)
expect(instance.group_names_access_map).to eq(expected) expect(group_access_instance.group_names_access_map).to eq(expected)
end end
end end
@ -403,9 +403,9 @@ RSpec.shared_examples 'HasGroups' do
group_read.id => ['read'], group_read.id => ['read'],
} }
instance.group_ids_access_map = expected group_access_instance.group_ids_access_map = expected
result = instance.attributes_with_association_ids result = group_access_instance.attributes_with_association_ids
expect(result['group_ids']).to eq(expected) expect(result['group_ids']).to eq(expected)
end end
end end
@ -418,9 +418,9 @@ RSpec.shared_examples 'HasGroups' do
group_read.id => ['read'], group_read.id => ['read'],
} }
instance.group_ids_access_map = expected group_access_instance.group_ids_access_map = expected
result = instance.attributes_with_association_names result = group_access_instance.attributes_with_association_names
expect(result['group_ids']).to eq(expected) expect(result['group_ids']).to eq(expected)
end end
@ -430,77 +430,77 @@ RSpec.shared_examples 'HasGroups' do
group_read.name => ['read'], group_read.name => ['read'],
} }
instance.group_names_access_map = expected group_access_instance.group_names_access_map = expected
result = instance.attributes_with_association_names result = group_access_instance.attributes_with_association_names
expect(result['groups']).to eq(expected) expect(result['groups']).to eq(expected)
end end
end end
context '.group_access_ids' do
before(:each) do
instance.group_names_access_map = {
group_read.name => 'read',
}
end
it 'responds to group_access_ids' do
expect(described_class).to respond_to(:group_access_ids)
end
it 'lists only active instance IDs' do
instance_inactive.group_names_access_map = {
group_read.name => 'read',
}
result = described_class.group_access_ids(group_read.id, 'read')
expect(result).not_to include(instance_inactive.id)
end
context 'Group ID parameter' do
include_examples '.group_access_ids call' do
let(:group_parameter) { group_read.id }
end
end
context 'Group parameter' do
include_examples '.group_access_ids call' do
let(:group_parameter) { group_read.id }
end
end
end
context '.group_access' do context '.group_access' do
it 'responds to group_access' do it 'responds to group_access' do
expect(described_class).to respond_to(:group_access) expect(described_class).to respond_to(:group_access)
end end
it 'wraps .group_access_ids' do before(:each) do
expect(described_class).to receive(:group_access_ids) group_access_instance.group_names_access_map = {
described_class.group_access(group_read, 'read') group_read.name => 'read',
}
end end
it 'returns class instances' do it 'lists only active instances' do
instance.group_names_access_map = { group_access_instance_inactive.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
result = described_class.group_access(group_read, 'read') result = described_class.group_access(group_read.id, 'read')
expect(result).to include(instance) expect(result).not_to include(group_access_instance_inactive)
end
context 'Group ID parameter' do
include_examples '.group_access call' do
let(:group_parameter) { group_read.id }
end
end
context 'Group parameter' do
include_examples '.group_access call' do
let(:group_parameter) { group_read }
end
end
end
context '.group_access_ids' do
it 'responds to group_access_ids' do
expect(described_class).to respond_to(:group_access_ids)
end
it 'wraps .group_access' do
expect(described_class).to receive(:group_access).and_call_original
described_class.group_access_ids(group_read, 'read')
end
it 'returns class instances' do
group_access_instance.group_names_access_map = {
group_read.name => 'read',
}
result = described_class.group_access_ids(group_read, 'read')
expect(result).to include(group_access_instance.id)
end end
end end
it 'destroys relations before instance gets destroyed' do it 'destroys relations before instance gets destroyed' do
instance.group_names_access_map = { group_access_instance.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => 'read', group_read.name => 'read',
group_inactive.name => 'write', group_inactive.name => 'write',
} }
expect do expect do
instance.destroy group_access_instance.destroy
end.to change { end.to change {
described_class.group_through.klass.count described_class.group_through.klass.count
}.by(-3) }.by(-3)
@ -512,46 +512,46 @@ RSpec.shared_examples '#group_access? call' do
context 'single access' do context 'single access' do
it 'checks positive' do it 'checks positive' do
expect(instance.group_access?(group_parameter, 'read')).to be true expect(group_access_instance.group_access?(group_parameter, 'read')).to be true
end end
it 'checks negative' do it 'checks negative' do
expect(instance.group_access?(group_parameter, 'write')).to be false expect(group_access_instance.group_access?(group_parameter, 'write')).to be false
end end
end end
context 'access list' do context 'access list' do
it 'checks positive' do it 'checks positive' do
expect(instance.group_access?(group_parameter, %w(read write))).to be true expect(group_access_instance.group_access?(group_parameter, %w(read write))).to be true
end end
it 'checks negative' do it 'checks negative' do
expect(instance.group_access?(group_parameter, %w(write create))).to be false expect(group_access_instance.group_access?(group_parameter, %w(write create))).to be false
end end
end end
end end
RSpec.shared_examples '.group_access_ids call' do RSpec.shared_examples '.group_access call' do
context 'single access' do context 'single access' do
it 'lists access IDs' do it 'lists access IDs' do
expect(described_class.group_access_ids(group_parameter, 'read')).to include(instance.id) expect(described_class.group_access(group_parameter, 'read')).to include(group_access_instance)
end end
it 'excludes non access IDs' do it 'excludes non access IDs' do
expect(described_class.group_access_ids(group_parameter, 'write')).not_to include(instance.id) expect(described_class.group_access(group_parameter, 'write')).not_to include(group_access_instance)
end end
end end
context 'access list' do context 'access list' do
it 'lists access IDs' do it 'lists access IDs' do
expect(described_class.group_access_ids(group_parameter, %w(read write))).to include(instance.id) expect(described_class.group_access(group_parameter, %w(read write))).to include(group_access_instance)
end end
it 'excludes non access IDs' do it 'excludes non access IDs' do
expect(described_class.group_access_ids(group_parameter, %w(write create))).not_to include(instance.id) expect(described_class.group_access(group_parameter, %w(write create))).not_to include(group_access_instance)
end end
end end
end end

View file

@ -0,0 +1,79 @@
# Requires: let(:group_access_no_permission_instance) { ... }
RSpec.shared_examples 'HasGroups and Permissions' do
context 'group' do
let(:group_read) { create(:group) }
before(:each) do
group_access_no_permission_instance.group_names_access_map = {
group_read.name => 'read',
}
end
context '#group_access?' do
it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.group_access?(group_read, 'read')).to be false
end
end
context '#group_ids_access' do
it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.group_ids_access('read')).to be_empty
end
end
context '#groups_access' do
it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.groups_access('read')).to be_empty
end
end
context '#group_names_access_map' do
it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.group_names_access_map).to be_empty
end
end
context '#group_ids_access_map' do
it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.group_ids_access_map).to be_empty
end
end
context '#attributes_with_association_ids' do
it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.attributes_with_association_ids['group_ids']).to be_empty
end
end
context '#attributes_with_association_names' do
it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.attributes_with_association_names['group_ids']).to be_empty
end
end
context '.group_access' do
it 'prevents instances without permissions' do
result = described_class.group_access(group_read.id, 'read')
expect(result).not_to include(group_access_no_permission_instance)
end
end
context '.group_access_ids' do
it 'prevents instances without permissions' do
result = described_class.group_access(group_read.id, 'read')
expect(result).not_to include(group_access_no_permission_instance.id)
end
end
end
end

View file

@ -1,10 +1,13 @@
# Requires: let(:group_access_instance) { ... }
# Requires: let(:new_group_access_instance) { ... }
RSpec.shared_examples 'HasRoles' do RSpec.shared_examples 'HasRoles' do
context 'role' do context 'role' do
let(:factory_name) { described_class.name.downcase.to_sym } let(:group_access_instance_inactive) {
let(:instance) { create(factory_name) } group_access_instance.update_attribute(:active, false)
let(:instance_inactive) { create(factory_name, active: false) } group_access_instance
}
let(:role) { create(:role) } let(:role) { create(:role) }
let(:group_instance) { create(:group) } let(:group_instance) { create(:group) }
let(:group_role) { create(:group) } let(:group_role) { create(:group) }
@ -12,36 +15,39 @@ RSpec.shared_examples 'HasRoles' do
context '#role_access?' do context '#role_access?' do
before(:each) do
role.group_names_access_map = {
group_role.name => 'read',
}
instance.roles = [role]
end
it 'responds to role_access?' do it 'responds to role_access?' do
expect(instance).to respond_to(:role_access?) expect(group_access_instance).to respond_to(:role_access?)
end end
context 'Group ID parameter' do context 'active Role' do
include_examples '#role_access? call' do before(:each) do
let(:group_parameter) { group_role.id } role.group_names_access_map = {
group_role.name => 'read',
}
group_access_instance.roles.push(role)
group_access_instance.save
end end
end
context 'Group parameter' do context 'Group ID parameter' do
include_examples '#role_access? call' do include_examples '#role_access? call' do
let(:group_parameter) { group_role } let(:group_parameter) { group_role.id }
end
end end
end
it 'prevents inactive Group' do context 'Group parameter' do
role.group_names_access_map = { include_examples '#role_access? call' do
group_inactive.name => 'read', let(:group_parameter) { group_role }
} end
end
expect(instance.group_access?(group_inactive.id, 'read')).to be false it 'prevents inactive Group' do
role.group_names_access_map = {
group_inactive.name => 'read',
}
expect(group_access_instance.group_access?(group_inactive.id, 'read')).to be false
end
end end
it 'prevents inactive Role' do it 'prevents inactive Role' do
@ -50,9 +56,10 @@ RSpec.shared_examples 'HasRoles' do
group_role.name => 'read', group_role.name => 'read',
} }
instance.roles = [role_inactive] group_access_instance.roles.push(role_inactive)
group_access_instance.save
expect(instance.group_access?(group_role.id, 'read')).to be false expect(group_access_instance.group_access?(group_role.id, 'read')).to be false
end end
end end
@ -63,7 +70,8 @@ RSpec.shared_examples 'HasRoles' do
group_role.name => 'read', group_role.name => 'read',
} }
instance.roles = [role] group_access_instance.roles.push(role)
group_access_instance.save
end end
it 'responds to role_access_ids' do it 'responds to role_access_ids' do
@ -75,19 +83,12 @@ RSpec.shared_examples 'HasRoles' do
group_role.name => 'read', group_role.name => 'read',
} }
result = described_class.group_access_ids(group_role.id, 'read') group_access_instance_inactive.roles.push(role)
expect(result).not_to include(instance_inactive.id) group_access_instance_inactive.save
end group_access_instance_inactive.save
it 'lists only active instance IDs' do
role.group_names_access_map = {
group_role.name => 'read',
}
instance_inactive.roles = [role]
result = described_class.role_access_ids(group_role.id, 'read') result = described_class.role_access_ids(group_role.id, 'read')
expect(result).not_to include(instance_inactive.id) expect(result).not_to include(group_access_instance_inactive.id)
end end
context 'Group ID parameter' do context 'Group ID parameter' do
@ -98,7 +99,7 @@ RSpec.shared_examples 'HasRoles' do
context 'Group parameter' do context 'Group parameter' do
include_examples '.role_access_ids call' do include_examples '.role_access_ids call' do
let(:group_parameter) { group_role.id } let(:group_parameter) { group_role }
end end
end end
end end
@ -110,9 +111,10 @@ RSpec.shared_examples 'HasRoles' do
group_role.name => 'read', group_role.name => 'read',
} }
instance.roles = [role] group_access_instance.roles.push(role)
group_access_instance.save
instance.group_names_access_map = { group_access_instance.group_names_access_map = {
group_instance.name => 'read', group_instance.name => 'read',
} }
end end
@ -120,13 +122,13 @@ RSpec.shared_examples 'HasRoles' do
context '#group_access?' do context '#group_access?' do
it 'falls back to #role_access?' do it 'falls back to #role_access?' do
expect(instance).to receive(:role_access?) expect(group_access_instance).to receive(:role_access?)
instance.group_access?(group_role, 'read') group_access_instance.group_access?(group_role, 'read')
end end
it "doesn't fall back to #role_access? if not needed" do it "doesn't fall back to #role_access? if not needed" do
expect(instance).not_to receive(:role_access?) expect(group_access_instance).not_to receive(:role_access?)
instance.group_access?(group_instance, 'read') group_access_instance.group_access?(group_instance, 'read')
end end
end end
@ -137,9 +139,10 @@ RSpec.shared_examples 'HasRoles' do
group_role.name => 'read', group_role.name => 'read',
} }
instance.roles = [role] group_access_instance.roles.push(role)
group_access_instance.save
instance.group_names_access_map = { group_access_instance.group_names_access_map = {
group_instance.name => 'read', group_instance.name => 'read',
} }
end end
@ -150,28 +153,28 @@ RSpec.shared_examples 'HasRoles' do
group_inactive.name => 'read', group_inactive.name => 'read',
} }
result = instance.group_ids_access('read') result = group_access_instance.group_ids_access('read')
expect(result).not_to include(group_inactive.id) expect(result).not_to include(group_inactive.id)
end end
context 'single access' do context 'single access' do
it 'lists access Group IDs' do it 'lists access Group IDs' do
result = instance.group_ids_access('read') result = group_access_instance.group_ids_access('read')
expect(result).to include(group_role.id) expect(result).to include(group_role.id)
end end
it "doesn't list for no access" do it "doesn't list for no access" do
result = instance.group_ids_access('write') result = group_access_instance.group_ids_access('write')
expect(result).not_to include(group_role.id) expect(result).not_to include(group_role.id)
end end
it "doesn't contain duplicate IDs" do it "doesn't contain duplicate IDs" do
instance.group_names_access_map = { group_access_instance.group_names_access_map = {
group_role.name => 'read', group_role.name => 'read',
} }
result = instance.group_ids_access('read') result = group_access_instance.group_ids_access('read')
expect(result.uniq).to eq(result) expect(result.uniq).to eq(result)
end end
end end
@ -179,21 +182,21 @@ RSpec.shared_examples 'HasRoles' do
context 'access list' do context 'access list' do
it 'lists access Group IDs' do it 'lists access Group IDs' do
result = instance.group_ids_access(%w(read write)) result = group_access_instance.group_ids_access(%w(read write))
expect(result).to include(group_role.id) expect(result).to include(group_role.id)
end end
it "doesn't list for no access" do it "doesn't list for no access" do
result = instance.group_ids_access(%w(write create)) result = group_access_instance.group_ids_access(%w(write create))
expect(result).not_to include(group_role.id) expect(result).not_to include(group_role.id)
end end
it "doesn't contain duplicate IDs" do it "doesn't contain duplicate IDs" do
instance.group_names_access_map = { group_access_instance.group_names_access_map = {
group_role.name => 'read', group_role.name => 'read',
} }
result = instance.group_ids_access(%w(read create)) result = group_access_instance.group_ids_access(%w(read create))
expect(result.uniq).to eq(result) expect(result.uniq).to eq(result)
end end
end end
@ -203,11 +206,11 @@ RSpec.shared_examples 'HasRoles' do
it 'includes the result of .role_access_ids' do it 'includes the result of .role_access_ids' do
result = described_class.group_access_ids(group_role, 'read') result = described_class.group_access_ids(group_role, 'read')
expect(result).to include(instance.id) expect(result).to include(group_access_instance.id)
end end
it "doesn't contain duplicate IDs" do it "doesn't contain duplicate IDs" do
instance.group_names_access_map = { group_access_instance.group_names_access_map = {
group_role.name => 'read', group_role.name => 'read',
} }
@ -223,22 +226,22 @@ RSpec.shared_examples '#role_access? call' do
context 'single access' do context 'single access' do
it 'checks positive' do it 'checks positive' do
expect(instance.role_access?(group_parameter, 'read')).to be true expect(group_access_instance.role_access?(group_parameter, 'read')).to be true
end end
it 'checks negative' do it 'checks negative' do
expect(instance.role_access?(group_parameter, 'write')).to be false expect(group_access_instance.role_access?(group_parameter, 'write')).to be false
end end
end end
context 'access list' do context 'access list' do
it 'checks positive' do it 'checks positive' do
expect(instance.role_access?(group_parameter, %w(read write))).to be true expect(group_access_instance.role_access?(group_parameter, %w(read write))).to be true
end end
it 'checks negative' do it 'checks negative' do
expect(instance.role_access?(group_parameter, %w(write create))).to be false expect(group_access_instance.role_access?(group_parameter, %w(write create))).to be false
end end
end end
end end
@ -247,22 +250,22 @@ RSpec.shared_examples '.role_access_ids call' do
context 'single access' do context 'single access' do
it 'lists access IDs' do it 'lists access IDs' do
expect(described_class.role_access_ids(group_parameter, 'read')).to include(instance.id) expect(described_class.role_access_ids(group_parameter, 'read')).to include(group_access_instance.id)
end end
it 'excludes non access IDs' do it 'excludes non access IDs' do
expect(described_class.role_access_ids(group_parameter, 'write')).not_to include(instance.id) expect(described_class.role_access_ids(group_parameter, 'write')).not_to include(group_access_instance.id)
end end
end end
context 'access list' do context 'access list' do
it 'lists access IDs' do it 'lists access IDs' do
expect(described_class.role_access_ids(group_parameter, %w(read write))).to include(instance.id) expect(described_class.role_access_ids(group_parameter, %w(read write))).to include(group_access_instance.id)
end end
it 'excludes non access IDs' do it 'excludes non access IDs' do
expect(described_class.role_access_ids(group_parameter, %w(write create))).not_to include(instance.id) expect(described_class.role_access_ids(group_parameter, %w(write create))).not_to include(group_access_instance.id)
end end
end end
end end

View file

@ -2,5 +2,8 @@ require 'rails_helper'
require 'models/concerns/has_groups_examples' require 'models/concerns/has_groups_examples'
RSpec.describe Role do RSpec.describe Role do
let(:group_access_instance) { create(:role) }
let(:new_group_access_instance) { build(:role) }
include_examples 'HasGroups' include_examples 'HasGroups'
end end

View file

@ -1,10 +1,17 @@
require 'rails_helper' require 'rails_helper'
require 'models/concerns/has_groups_examples' require 'models/concerns/has_groups_examples'
require 'models/concerns/has_roles_examples' require 'models/concerns/has_roles_examples'
require 'models/concerns/has_groups_permissions_examples'
RSpec.describe User do RSpec.describe User do
let(:group_access_instance) { create(:user, roles: [Role.find_by(name: 'Agent')]) }
let(:new_group_access_instance) { build(:user, roles: [Role.find_by(name: 'Agent')]) }
let(:group_access_no_permission_instance) { build(:user) }
include_examples 'HasGroups' include_examples 'HasGroups'
include_examples 'HasRoles' include_examples 'HasRoles'
include_examples 'HasGroups and Permissions'
let(:new_password) { 'N3W54V3PW!' } let(:new_password) { 'N3W54V3PW!' }