From 867b36baa8f0cb0be33d7a8b5f7b804384f5a3d9 Mon Sep 17 00:00:00 2001 From: Rolf Schmidt Date: Wed, 22 Sep 2021 16:57:27 +0200 Subject: [PATCH] Maintenance: Add assets level to have different data sets based on permissions --- .../application_model/can_associations.rb | 11 +- app/models/group.rb | 2 + app/models/group/assets.rb | 14 ++ app/models/object_manager/element/backend.rb | 2 +- app/models/organization/assets.rb | 7 + app/models/role/assets.rb | 8 + app/models/user/assets.rb | 15 ++ lib/session_helper.rb | 2 +- lib/user_info.rb | 5 + lib/user_info/assets.rb | 52 ++++++ spec/system/basic/assets_spec.rb | 172 ++++++++++++++++++ 11 files changed, 284 insertions(+), 6 deletions(-) create mode 100644 app/models/group/assets.rb create mode 100644 lib/user_info/assets.rb create mode 100644 spec/system/basic/assets_spec.rb diff --git a/app/models/application_model/can_associations.rb b/app/models/application_model/can_associations.rb index c615ab4d2..41cc0639a 100644 --- a/app/models/application_model/can_associations.rb +++ b/app/models/application_model/can_associations.rb @@ -121,7 +121,7 @@ returns key = "#{self.class}::aws::#{id}" cache = Cache.read(key) - return cache if cache + return filter_unauthorized_attributes(cache) if cache attributes = self.attributes relevant = %i[has_and_belongs_to_many has_many] @@ -160,7 +160,7 @@ returns filter_attributes(attributes) Cache.write(key, attributes) - attributes + filter_unauthorized_attributes(attributes) end =begin @@ -234,8 +234,7 @@ returns end filter_attributes(attributes) - - attributes + filter_unauthorized_attributes(attributes) end def filter_attributes(attributes) @@ -243,6 +242,10 @@ returns attributes.except!('password', 'token', 'tokens', 'token_ids') end + def filter_unauthorized_attributes(attributes) + attributes + end + =begin reference if association id check diff --git a/app/models/group.rb b/app/models/group.rb index 352722566..a3150d0e3 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -12,6 +12,8 @@ class Group < ApplicationModel include HasTicketCreateScreenImpact include HasSearchIndexBackend + include Group::Assets + belongs_to :email_address, optional: true belongs_to :signature, optional: true diff --git a/app/models/group/assets.rb b/app/models/group/assets.rb new file mode 100644 index 000000000..058a9ee70 --- /dev/null +++ b/app/models/group/assets.rb @@ -0,0 +1,14 @@ +# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/ + +class Group + module Assets + extend ActiveSupport::Concern + + def filter_unauthorized_attributes(attributes) + return super if UserInfo.assets.blank? || UserInfo.assets.agent? + + attributes = super + attributes.slice('id', 'name', 'active') + end + end +end diff --git a/app/models/object_manager/element/backend.rb b/app/models/object_manager/element/backend.rb index 9851f14f4..0af905eec 100644 --- a/app/models/object_manager/element/backend.rb +++ b/app/models/object_manager/element/backend.rb @@ -43,7 +43,7 @@ class ObjectManager::Element::Backend end def screens - attribute.screens.transform_values do |permission_options| + @screens ||= attribute.screens.transform_values do |permission_options| screen_value(permission_options) end end diff --git a/app/models/organization/assets.rb b/app/models/organization/assets.rb index 10fcb9572..37b2ab700 100644 --- a/app/models/organization/assets.rb +++ b/app/models/organization/assets.rb @@ -70,5 +70,12 @@ returns end data end + + def filter_unauthorized_attributes(attributes) + return super if UserInfo.assets.blank? || UserInfo.assets.agent? + + attributes = super + attributes.slice('id', 'name', 'active') + end end end diff --git a/app/models/role/assets.rb b/app/models/role/assets.rb index 6f002cb6a..8780602a6 100644 --- a/app/models/role/assets.rb +++ b/app/models/role/assets.rb @@ -60,5 +60,13 @@ returns end data end + + def filter_unauthorized_attributes(attributes) + return super if UserInfo.assets.blank? || UserInfo.assets.agent? + + attributes = super + attributes['name'] = "Role_#{id}" + attributes.slice('id', 'name', 'group_ids', 'permission_ids', 'active') + end end end diff --git a/app/models/user/assets.rb b/app/models/user/assets.rb index ec0db2238..ba00c4f91 100644 --- a/app/models/user/assets.rb +++ b/app/models/user/assets.rb @@ -110,5 +110,20 @@ returns end data end + + def filter_unauthorized_attributes(attributes) + return super if UserInfo.assets.blank? || UserInfo.assets.agent? + + # customer assets for the user session + if UserInfo.current_user_id == id + attributes = super + attributes.except!('web', 'phone', 'mobile', 'fax', 'department', 'street', 'zip', 'city', 'country', 'address', 'note') + return attributes + end + + # customer assets for other user + attributes = super + attributes.slice('id', 'firstname', 'lastname', 'image', 'image_source', 'active') + end end end diff --git a/lib/session_helper.rb b/lib/session_helper.rb index 9b3f8c826..5fb10ed8e 100644 --- a/lib/session_helper.rb +++ b/lib/session_helper.rb @@ -4,7 +4,7 @@ module SessionHelper def self.json_hash(user) collections, assets = default_collections(user) { - session: user.filter_attributes(user.attributes), + session: user.filter_unauthorized_attributes(user.filter_attributes(user.attributes)), models: models(user), collections: collections, assets: assets, diff --git a/lib/user_info.rb b/lib/user_info.rb index 833177a86..fecdfed7a 100644 --- a/lib/user_info.rb +++ b/lib/user_info.rb @@ -7,6 +7,11 @@ module UserInfo def self.current_user_id=(user_id) Thread.current[:user_id] = user_id + Thread.current[:assets] = UserInfo::Assets.new(user_id) + end + + def self.assets + Thread.current[:assets] end def self.ensure_current_user_id diff --git a/lib/user_info/assets.rb b/lib/user_info/assets.rb new file mode 100644 index 000000000..01df38c32 --- /dev/null +++ b/lib/user_info/assets.rb @@ -0,0 +1,52 @@ +# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/ + +class UserInfo::Assets + LEVEL_CUSTOMER = 1 + LEVEL_AGENT = 2 + LEVEL_ADMIN = 3 + + attr_accessor :current_user_id, :level, :filter_attributes, :user + + def initialize(current_user_id) + @current_user_id = current_user_id + @user = User.find_by(id: current_user_id) if current_user_id.present? + + set_level + end + + def admin? + check_level?(UserInfo::Assets::LEVEL_ADMIN) + end + + def agent? + check_level?(UserInfo::Assets::LEVEL_AGENT) + end + + def customer? + check_level?(UserInfo::Assets::LEVEL_CUSTOMER) + end + + def set_level + if user.blank? + self.level = nil + return + end + + self.level = UserInfo::Assets::LEVEL_CUSTOMER + Permission.where(id: user.permissions_with_child_ids).each do |permission| + case permission.name + when %r{^admin\.} + self.level = UserInfo::Assets::LEVEL_ADMIN + break + when 'ticket.agent' + self.level = UserInfo::Assets::LEVEL_AGENT + end + end + end + + def check_level?(check) + return true if user.blank? + + level >= check + end +end diff --git a/spec/system/basic/assets_spec.rb b/spec/system/basic/assets_spec.rb new file mode 100644 index 000000000..e273ca74b --- /dev/null +++ b/spec/system/basic/assets_spec.rb @@ -0,0 +1,172 @@ +# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/ + +require 'rails_helper' + +RSpec.describe 'Assets', type: :system, db_strategy: :reset do + let(:organization) { create(:organization, note: 'hello') } + let(:customer) { create(:customer, organization: organization, note: 'hello', last_login: Time.zone.now, login_failed: 1) } + let(:agent) { create(:agent, groups: [Group.find_by(name: 'Users')], note: 'hello', last_login: Time.zone.now, login_failed: 1) } + let(:admin) { create(:admin, groups: [Group.find_by(name: 'Users')], note: 'hello', last_login: Time.zone.now, login_failed: 1) } + let(:ticket) { create(:ticket, owner: agent, group: Group.find_by(name: 'Users'), customer: customer, created_by: admin) } + + context 'groups' do + def group_note + page.execute_script('return App.Group.first().note') + end + + describe 'when customer', authenticated_as: :customer do + it 'can not access group details' do + expect(group_note).to be nil + end + end + + describe 'when agent', authenticated_as: :agent do + it 'can access group details' do + expect(group_note).not_to be nil + end + end + + describe 'when admin', authenticated_as: :admin do + it 'can access group details' do + expect(group_note).not_to be nil + end + end + end + + context 'organizations' do + def organization_note + page.execute_script("return App.Organization.find(#{organization.id}).note") + end + + before do + visit "#ticket/zoom/#{ticket.id}" + end + + describe 'when customer', authenticated_as: :customer do + it 'can not access organization details' do + expect(organization_note).to be nil + end + end + + describe 'when agent', authenticated_as: :agent do + it 'can access organization details' do + expect(organization_note).not_to be nil + end + end + + describe 'when admin', authenticated_as: :admin do + it 'can access organization details' do + expect(organization_note).not_to be nil + end + end + end + + context 'roles' do + def role_name + page.execute_script('return App.Role.first().name') + end + + before do + visit "#ticket/zoom/#{ticket.id}" + end + + describe 'when customer', authenticated_as: :customer do + it 'can not access role details' do + expect(role_name).to eq('Role_1') + end + end + + describe 'when agent', authenticated_as: :agent do + it 'can access role details' do + expect(role_name).not_to eq('Role_1') + end + end + + describe 'when admin', authenticated_as: :admin do + it 'can access role details' do + expect(role_name).not_to eq('Role_1') + end + end + end + + context 'users' do + def customer_email + page.execute_script("return App.User.find(#{customer.id}).email") + end + + def customer_note + page.execute_script("return App.User.find(#{customer.id}).note") + end + + def owner_firstname + page.execute_script("return App.User.find(#{agent.id}).firstname") + end + + def owner_details + [ + page.execute_script("return App.User.find(#{agent.id}).last_login"), + page.execute_script("return App.User.find(#{agent.id}).login_failed"), + page.execute_script("return App.User.find(#{agent.id}).email"), + page.execute_script("return App.User.find(#{agent.id}).note"), + ].compact + end + + before do + visit "#ticket/zoom/#{ticket.id}" + end + + describe 'when customer', authenticated_as: :customer do + it 'can access customer email' do + expect(customer_email).not_to be nil + end + + it 'can not access customer note' do + expect(customer_note).to be nil + end + + it 'can not access owner details' do + expect(owner_details).to be_empty + end + + it 'can access owner firstname' do + expect(owner_firstname).not_to be nil + end + end + + describe 'when agent', authenticated_as: :agent do + it 'can access customer email' do + expect(customer_email).not_to be nil + end + + it 'can access customer note' do + expect(customer_note).not_to be nil + end + + it 'can access owner details' do + expect(owner_details).not_to be_empty + end + + it 'can access owner firstname' do + expect(owner_firstname).not_to be nil + end + end + + describe 'when admin', authenticated_as: :admin do + it 'can access customer email' do + expect(customer_email).not_to be nil + end + + it 'can access customer note' do + expect(customer_note).not_to be nil + end + + it 'can access owner details' do + expect(owner_details).not_to be_empty + end + + it 'can access owner firstname' do + expect(owner_firstname).not_to be nil + end + end + end +end