diff --git a/app/models/concerns/has_groups.rb b/app/models/concerns/has_groups.rb index c5be27977..b65c6b372 100644 --- a/app/models/concerns/has_groups.rb +++ b/app/models/concerns/has_groups.rb @@ -7,8 +7,8 @@ module HasGroups attr_accessor :group_access_buffer - after_create :check_group_access_buffer - after_update :check_group_access_buffer + after_create :check_group_access_buffer + after_update :check_group_access_buffer association_attributes_ignored :groups @@ -61,6 +61,9 @@ module HasGroups # # @return [Boolean] def group_access?(group_id, access) + return false if !active? + return false if !groups_access_permission? + group_id = self.class.ensure_group_id_parameter(group_id) access = self.class.ensure_group_access_list_parameter(access) @@ -92,8 +95,10 @@ module HasGroups # # @return [Array] Group IDs the instance has the given access(es) to. def group_ids_access(access) - access = self.class.ensure_group_access_list_parameter(access) + return [] if !active? + return [] if !groups_access_permission? + access = self.class.ensure_group_access_list_parameter(access) foreign_key = group_through.foreign_key klass = group_through.klass @@ -124,6 +129,8 @@ module HasGroups # # @return [Array] Groups the instance has the given access(es) to. def groups_access(access) + return [] if !active? + return [] if !groups_access_permission? group_ids = group_ids_access(access) Group.where(id: group_ids) end @@ -179,9 +186,24 @@ module HasGroups @group_through ||= self.class.group_through 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 def groups_access_map(key) + return {} if !active? + return {} if !groups_access_permission? + {}.tap do |hash| groups.access.where(active: true).pluck(key, :access).each do |entry| hash[ entry[0] ] ||= [] @@ -257,20 +279,7 @@ module HasGroups # # @return [Array] def group_access_ids(group_id, access) - group_id = ensure_group_id_parameter(group_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 + group_access(group_id, access).collect(&:id) end # Lists instances having the given access(es) to the given Group. @@ -289,8 +298,22 @@ module HasGroups # # @return [Array] def group_access(group_id, access) - instance_ids = group_access_ids(group_id, access) - where(id: instance_ids) + group_id = ensure_group_id_parameter(group_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 ||= [] + + # 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 # The reflection instance containing the association data diff --git a/app/models/concerns/has_roles.rb b/app/models/concerns/has_roles.rb index bc3c75b72..42377e6a2 100644 --- a/app/models/concerns/has_roles.rb +++ b/app/models/concerns/has_roles.rb @@ -18,6 +18,8 @@ module HasRoles # # @return [Boolean] def role_access?(group_id, access) + return false if !groups_access_permission? + group_id = self.class.ensure_group_id_parameter(group_id) 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 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] + 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. # # @example Group ID param @@ -53,12 +79,7 @@ module HasRoles # # @return [Array] def role_access_ids(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 - includes(:roles).where(active: true, join_table => { role_id: role_ids }).distinct.pluck(:id) + role_access(group_id, access).collect(&:id) end def ensure_group_id_parameter(group_or_id) diff --git a/app/models/ticket.rb b/app/models/ticket.rb index ecb483310..129a524d7 100644 --- a/app/models/ticket.rb +++ b/app/models/ticket.rb @@ -768,7 +768,7 @@ perform changes on ticket email = User.lookup(id: owner_id).email recipients_raw.push(email) 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) end else diff --git a/app/models/transaction/notification.rb b/app/models/transaction/notification.rb index f8880c359..ba0267b9c 100644 --- a/app/models/transaction/notification.rb +++ b/app/models/transaction/notification.rb @@ -47,10 +47,11 @@ class Transaction::Notification recipients_and_channels = [] # 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 possible_recipients.push ticket.owner end + already_checked_recipient_ids = {} possible_recipients.each { |user| result = NotificationFactory::Mailer.notification_settings(user, ticket, @item[:type]) diff --git a/spec/models/concerns/has_groups_examples.rb b/spec/models/concerns/has_groups_examples.rb index 0fd9b3f43..4a91dec86 100644 --- a/spec/models/concerns/has_groups_examples.rb +++ b/spec/models/concerns/has_groups_examples.rb @@ -1,10 +1,12 @@ +# Requires: let(:group_access_instance) { ... } +# Requires: let(:new_group_access_instance) { ... } RSpec.shared_examples 'HasGroups' do context 'group' do - - let(:factory_name) { described_class.name.downcase.to_sym } - let(:instance) { create(factory_name) } - let(:instance_inactive) { create(factory_name, active: false) } + let(:group_access_instance_inactive) { + group_access_instance.update_attribute(:active, false) + group_access_instance + } let(:group_full) { create(:group) } let(:group_read) { create(:group) } let(:group_inactive) { create(:group, active: false) } @@ -20,7 +22,7 @@ RSpec.shared_examples 'HasGroups' do end 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 @@ -38,19 +40,19 @@ RSpec.shared_examples 'HasGroups' do context '#groups' do it 'responds to groups' do - expect(instance).to respond_to(:groups) + expect(group_access_instance).to respond_to(:groups) end context '#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 context 'result' do before(:each) do - instance.group_names_access_map = { + group_access_instance.group_names_access_map = { group_full.name => 'full', group_read.name => 'read', group_inactive.name => 'write', @@ -58,23 +60,23 @@ RSpec.shared_examples 'HasGroups' do end 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 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 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 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 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 @@ -82,14 +84,14 @@ RSpec.shared_examples 'HasGroups' do context '#group_access?' do - before(:each) do - instance.group_names_access_map = { - group_read.name => 'read', - } + it 'responds to group_access?' do + expect(group_access_instance).to respond_to(:group_access?) end - it 'responds to group_access?' do - expect(instance).to respond_to(:group_access?) + before(:each) do + group_access_instance.group_names_access_map = { + group_read.name => 'read', + } end context 'Group ID parameter' do @@ -105,45 +107,61 @@ RSpec.shared_examples 'HasGroups' do end it 'prevents inactive Group' do - instance.group_names_access_map = { + group_access_instance.group_names_access_map = { 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 + + it 'prevents inactive instances' do + group_access_instance_inactive.group_names_access_map = { + group_read.name => 'read', + } + + expect(group_access_instance_inactive.group_access?(group_read.id, 'read')).to be false end end 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 - instance.group_names_access_map = { + group_access_instance.group_names_access_map = { group_read.name => 'read', } end - it 'responds to group_ids_access' do - expect(instance).to respond_to(:group_ids_access) - end - it 'lists only active Group IDs' do - instance.group_names_access_map = { + group_access_instance.group_names_access_map = { group_read.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) end + it "doesn't list for inactive instances" do + group_access_instance_inactive.group_names_access_map = { + group_read.name => 'read', + } + + expect(group_access_instance_inactive.group_ids_access('read')).to be_empty + end + context 'single access' 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) end 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) end end @@ -151,12 +169,12 @@ RSpec.shared_examples 'HasGroups' do context 'access list' 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) end 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) end end @@ -165,19 +183,19 @@ RSpec.shared_examples 'HasGroups' do context '#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 it 'wraps #group_ids_access' do - expect(instance).to receive(:group_ids_access) - instance.groups_access('read') + expect(group_access_instance).to receive(:group_ids_access) + group_access_instance.groups_access('read') end it 'returns Groups' do - instance.group_names_access_map = { + group_access_instance.group_names_access_map = { group_read.name => 'read', } - result = instance.groups_access('read') + result = group_access_instance.groups_access('read') expect(result).to include(group_read) end end @@ -185,14 +203,14 @@ RSpec.shared_examples 'HasGroups' do context '#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 - context 'Group name => access relation storage' do + context 'existing instance' do it 'stores Hash with String values' do expect do - instance.group_names_access_map = { + group_access_instance.group_names_access_map = { group_full.name => 'full', group_read.name => 'read', } @@ -201,9 +219,9 @@ RSpec.shared_examples 'HasGroups' do }.by(2) end - it 'stores Hash with String values' do + it 'stores Hash with Array values' do expect do - instance.group_names_access_map = { + group_access_instance.group_names_access_map = { group_full.name => 'full', group_read.name => %w(read write), } @@ -211,33 +229,32 @@ RSpec.shared_examples 'HasGroups' do described_class.group_through.klass.count }.by(3) end + end - context 'new instance' do - let(:new_instance) { build(factory_name) } + context 'new instance' do - it "doesn't store directly" do - expect do - new_instance.group_names_access_map = { - group_full.name => 'full', - group_read.name => 'read', - } - end.not_to change { - described_class.group_through.klass.count + it "doesn't store directly" do + expect do + new_group_access_instance.group_names_access_map = { + group_full.name => 'full', + group_read.name => 'read', } - end + end.not_to change { + described_class.group_through.klass.count + } + end - it 'stores after save' do - expect do - new_instance.group_names_access_map = { - group_full.name => 'full', - group_read.name => 'read', - } + it 'stores after save' do + expect do + new_group_access_instance.group_names_access_map = { + group_full.name => 'full', + group_read.name => 'read', + } - new_instance.save - end.to change { - described_class.group_through.klass.count - }.by(2) - end + new_group_access_instance.save + end.to change { + described_class.group_through.klass.count + }.by(2) end end end @@ -245,7 +262,7 @@ RSpec.shared_examples 'HasGroups' do context '#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 it 'returns instance Group name => access relations as Hash' do @@ -254,23 +271,32 @@ RSpec.shared_examples 'HasGroups' do 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 + + it "doesn't map for inactive instances" do + group_access_instance_inactive.group_names_access_map = { + group_full.name => ['full'], + group_read.name => ['read'], + } + + expect(group_access_instance_inactive.group_names_access_map).to be_empty end end context '#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 - context 'Group ID => access relation storage' do + context 'existing instance' do it 'stores Hash with String values' do expect do - instance.group_ids_access_map = { + group_access_instance.group_ids_access_map = { group_full.id => 'full', group_read.id => 'read', } @@ -281,7 +307,7 @@ RSpec.shared_examples 'HasGroups' do it 'stores Hash with String values' do expect do - instance.group_ids_access_map = { + group_access_instance.group_ids_access_map = { group_full.id => 'full', group_read.id => %w(read write), } @@ -289,33 +315,32 @@ RSpec.shared_examples 'HasGroups' do described_class.group_through.klass.count }.by(3) end + end - context 'new instance' do - let(:new_instance) { build(factory_name) } + context 'new instance' do - it "doesn't store directly" do - expect do - new_instance.group_ids_access_map = { - group_full.id => 'full', - group_read.id => 'read', - } - end.not_to change { - described_class.group_through.klass.count + it "doesn't store directly" do + expect do + new_group_access_instance.group_ids_access_map = { + group_full.id => 'full', + group_read.id => 'read', } - end + end.not_to change { + described_class.group_through.klass.count + } + end - it 'stores after save' do - expect do - new_instance.group_ids_access_map = { - group_full.id => 'full', - group_read.id => 'read', - } + it 'stores after save' do + expect do + new_group_access_instance.group_ids_access_map = { + group_full.id => 'full', + group_read.id => 'read', + } - new_instance.save - end.to change { - described_class.group_through.klass.count - }.by(2) - end + new_group_access_instance.save + end.to change { + described_class.group_through.klass.count + }.by(2) end end end @@ -323,7 +348,7 @@ RSpec.shared_examples 'HasGroups' do context '#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 it 'returns instance Group ID => access relations as Hash' do @@ -332,9 +357,18 @@ RSpec.shared_examples 'HasGroups' do 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 + + it "doesn't map for inactive instances" do + group_access_instance_inactive.group_ids_access_map = { + group_full.id => ['full'], + group_read.id => ['read'], + } + + expect(group_access_instance_inactive.group_ids_access_map).to be_empty end end @@ -346,8 +380,8 @@ RSpec.shared_examples 'HasGroups' do group_read.id => ['read'], } - instance.associations_from_param(group_ids: expected) - expect(instance.group_ids_access_map).to eq(expected) + group_access_instance.associations_from_param(group_ids: expected) + expect(group_access_instance.group_ids_access_map).to eq(expected) end it 'handles groups parameter as group_names_access_map' do @@ -356,8 +390,8 @@ RSpec.shared_examples 'HasGroups' do group_read.name => ['read'], } - instance.associations_from_param(groups: expected) - expect(instance.group_names_access_map).to eq(expected) + group_access_instance.associations_from_param(groups: expected) + expect(group_access_instance.group_names_access_map).to eq(expected) end end @@ -369,9 +403,9 @@ RSpec.shared_examples 'HasGroups' do 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) end end @@ -384,9 +418,9 @@ RSpec.shared_examples 'HasGroups' do 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) end @@ -396,77 +430,77 @@ RSpec.shared_examples 'HasGroups' do 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) 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 it 'responds to group_access' do expect(described_class).to respond_to(:group_access) end - it 'wraps .group_access_ids' do - expect(described_class).to receive(:group_access_ids) - described_class.group_access(group_read, 'read') + before(:each) do + group_access_instance.group_names_access_map = { + group_read.name => 'read', + } end - it 'returns class instances' do - instance.group_names_access_map = { + it 'lists only active instances' do + group_access_instance_inactive.group_names_access_map = { group_read.name => 'read', } - result = described_class.group_access(group_read, 'read') - expect(result).to include(instance) + result = described_class.group_access(group_read.id, 'read') + 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 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_read.name => 'read', group_inactive.name => 'write', } expect do - instance.destroy + group_access_instance.destroy end.to change { described_class.group_through.klass.count }.by(-3) @@ -478,46 +512,46 @@ RSpec.shared_examples '#group_access? call' do context 'single access' 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 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 context 'access list' 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 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 -RSpec.shared_examples '.group_access_ids call' do +RSpec.shared_examples '.group_access call' do context 'single access' 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 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 context 'access list' 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 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 diff --git a/spec/models/concerns/has_groups_permissions_examples.rb b/spec/models/concerns/has_groups_permissions_examples.rb new file mode 100644 index 000000000..0e66786be --- /dev/null +++ b/spec/models/concerns/has_groups_permissions_examples.rb @@ -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 diff --git a/spec/models/concerns/has_roles_examples.rb b/spec/models/concerns/has_roles_examples.rb index 83f5fe29c..c591c317b 100644 --- a/spec/models/concerns/has_roles_examples.rb +++ b/spec/models/concerns/has_roles_examples.rb @@ -1,10 +1,13 @@ +# Requires: let(:group_access_instance) { ... } +# Requires: let(:new_group_access_instance) { ... } RSpec.shared_examples 'HasRoles' do context 'role' do - let(:factory_name) { described_class.name.downcase.to_sym } - let(:instance) { create(factory_name) } - let(:instance_inactive) { create(factory_name, active: false) } + let(:group_access_instance_inactive) { + group_access_instance.update_attribute(:active, false) + group_access_instance + } let(:role) { create(:role) } let(:group_instance) { create(:group) } let(:group_role) { create(:group) } @@ -12,36 +15,39 @@ RSpec.shared_examples 'HasRoles' 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 - expect(instance).to respond_to(:role_access?) + expect(group_access_instance).to respond_to(:role_access?) end - context 'Group ID parameter' do - include_examples '#role_access? call' do - let(:group_parameter) { group_role.id } + context 'active Role' do + before(:each) do + role.group_names_access_map = { + group_role.name => 'read', + } + + group_access_instance.roles.push(role) + group_access_instance.save end - end - context 'Group parameter' do - include_examples '#role_access? call' do - let(:group_parameter) { group_role } + context 'Group ID parameter' do + include_examples '#role_access? call' do + let(:group_parameter) { group_role.id } + end end - end - it 'prevents inactive Group' do - role.group_names_access_map = { - group_inactive.name => 'read', - } + context 'Group parameter' do + include_examples '#role_access? call' do + 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 it 'prevents inactive Role' do @@ -50,9 +56,10 @@ RSpec.shared_examples 'HasRoles' do 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 @@ -63,7 +70,8 @@ RSpec.shared_examples 'HasRoles' do group_role.name => 'read', } - instance.roles = [role] + group_access_instance.roles.push(role) + group_access_instance.save end it 'responds to role_access_ids' do @@ -75,19 +83,12 @@ RSpec.shared_examples 'HasRoles' do group_role.name => 'read', } - result = described_class.group_access_ids(group_role.id, 'read') - expect(result).not_to include(instance_inactive.id) - end - - it 'lists only active instance IDs' do - role.group_names_access_map = { - group_role.name => 'read', - } - - instance_inactive.roles = [role] + group_access_instance_inactive.roles.push(role) + group_access_instance_inactive.save + group_access_instance_inactive.save 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 context 'Group ID parameter' do @@ -98,7 +99,7 @@ RSpec.shared_examples 'HasRoles' do context 'Group parameter' do include_examples '.role_access_ids call' do - let(:group_parameter) { group_role.id } + let(:group_parameter) { group_role } end end end @@ -110,9 +111,10 @@ RSpec.shared_examples 'HasRoles' do 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', } end @@ -120,13 +122,13 @@ RSpec.shared_examples 'HasRoles' do context '#group_access?' do it 'falls back to #role_access?' do - expect(instance).to receive(:role_access?) - instance.group_access?(group_role, 'read') + expect(group_access_instance).to receive(:role_access?) + group_access_instance.group_access?(group_role, 'read') end it "doesn't fall back to #role_access? if not needed" do - expect(instance).not_to receive(:role_access?) - instance.group_access?(group_instance, 'read') + expect(group_access_instance).not_to receive(:role_access?) + group_access_instance.group_access?(group_instance, 'read') end end @@ -137,9 +139,10 @@ RSpec.shared_examples 'HasRoles' do 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', } end @@ -150,28 +153,28 @@ RSpec.shared_examples 'HasRoles' do 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) end context 'single access' 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) end 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) end it "doesn't contain duplicate IDs" do - instance.group_names_access_map = { + group_access_instance.group_names_access_map = { group_role.name => 'read', } - result = instance.group_ids_access('read') + result = group_access_instance.group_ids_access('read') expect(result.uniq).to eq(result) end end @@ -179,21 +182,21 @@ RSpec.shared_examples 'HasRoles' do context 'access list' 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) end 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) end it "doesn't contain duplicate IDs" do - instance.group_names_access_map = { + group_access_instance.group_names_access_map = { 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) end end @@ -203,11 +206,11 @@ RSpec.shared_examples 'HasRoles' do it 'includes the result of .role_access_ids' do result = described_class.group_access_ids(group_role, 'read') - expect(result).to include(instance.id) + expect(result).to include(group_access_instance.id) end it "doesn't contain duplicate IDs" do - instance.group_names_access_map = { + group_access_instance.group_names_access_map = { group_role.name => 'read', } @@ -223,22 +226,22 @@ RSpec.shared_examples '#role_access? call' do context 'single access' 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 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 context 'access list' 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 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 @@ -247,22 +250,22 @@ RSpec.shared_examples '.role_access_ids call' do context 'single access' 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 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 context 'access list' 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 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 diff --git a/spec/models/role_spec.rb b/spec/models/role_spec.rb index 9e9d307d7..7f02ceb5c 100644 --- a/spec/models/role_spec.rb +++ b/spec/models/role_spec.rb @@ -2,5 +2,8 @@ require 'rails_helper' require 'models/concerns/has_groups_examples' RSpec.describe Role do + let(:group_access_instance) { create(:role) } + let(:new_group_access_instance) { build(:role) } + include_examples 'HasGroups' end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index de8985e17..8dc205a0f 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,10 +1,17 @@ require 'rails_helper' require 'models/concerns/has_groups_examples' require 'models/concerns/has_roles_examples' +require 'models/concerns/has_groups_permissions_examples' 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 'HasRoles' + include_examples 'HasGroups and Permissions' let(:new_password) { 'N3W54V3PW!' }