OpenProject is the leading open source project management software.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
openproject/spec/models/capabilities/scopes/default_spec.rb

316 lines
10 KiB

#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe Capabilities::Scopes::Default, type: :model do
# we focus on the non current user capabilities to make the tests easier to understand
subject(:scope) { Capability.default.where(principal_id: user.id) }
let(:permissions) { %i[] }
let(:global_permissions) { %i[] }
let(:non_member_permissions) { %i[] }
let(:project_public) { false }
let(:project_active) { true }
let!(:project) { FactoryBot.create(:project, public: project_public, active: project_active) }
let(:role) do
FactoryBot.create(:role, permissions: permissions)
end
let(:global_role) do
FactoryBot.create(:global_role, permissions: global_permissions)
end
let(:user_admin) { false }
let(:user_status) { Principal.statuses[:active] }
let(:current_user_admin) { true }
let!(:user) { FactoryBot.create(:user, admin: user_admin, status: user_status) }
let(:global_member) do
FactoryBot.create(:global_member,
principal: user,
roles: [global_role])
end
let(:member) do
FactoryBot.create(:member,
principal: user,
roles: [role],
project: project)
end
let(:non_member_role) do
FactoryBot.create(:non_member,
permissions: non_member_permissions)
end
let(:own_role) { FactoryBot.create(:role, permissions: [] )}
let(:own_member) do
FactoryBot.create(:member,
principal: current_user,
roles: [own_role],
project: project)
end
let(:members) { [] }
current_user do
FactoryBot.create(:user, admin: current_user_admin)
end
shared_examples_for 'consists of contract actions' do
it 'includes the expected for the scoped to user' do
expect(scope.pluck(:action, :principal_id, :context_id))
.to match_array(expected)
end
end
shared_examples_for 'is empty' do
it 'is empty for the scoped to user' do
expect(scope)
.to be_empty
end
end
describe '.default' do
before do
members
end
context 'without any members and non member roles' do
it_behaves_like 'is empty'
end
context 'with a member without a permission' do
let(:members) { [member] }
it_behaves_like 'is empty'
end
context 'with a global member without a permission' do
let(:members) { [global_member] }
it_behaves_like 'is empty'
end
context 'with a non member role without a permission' do
let(:members) { [non_member_role] }
it_behaves_like 'is empty'
end
context 'with a global member with an action permission' do
let(:global_permissions) { %i[manage_user] }
let(:members) { [global_member] }
it_behaves_like 'consists of contract actions' do
let(:expected) do
[['users/create', user.id, nil],
['users/read', user.id, nil],
['users/update', user.id, nil]]
end
end
end
context 'with a member with an action permission' do
let(:permissions) { %i[manage_members] }
let(:members) { [member] }
it_behaves_like 'consists of contract actions' do
let(:expected) do
[['memberships/create', user.id, project.id],
['memberships/destroy', user.id, project.id],
['memberships/update', user.id, project.id]]
end
end
end
context 'with a lgobal member with an action permission and the user being locked' do
let(:permissions) { %i[manage_members] }
let(:members) { [member] }
let(:user_status) { Principal.statuses[:locked] }
it_behaves_like 'is empty'
end
context 'with a member with an action permission and the user being locked' do
let(:permissions) { %i[manage_members] }
let(:members) { [member] }
let(:user_status) { Principal.statuses[:locked] }
it_behaves_like 'is empty'
end
context 'with the non member role with an action permission' do
let(:non_member_permissions) { %i[view_members] }
let(:members) { [non_member_role] }
context 'with the project being private' do
it_behaves_like 'is empty'
end
context 'with the project being public' do
let(:project_public) { true }
it_behaves_like 'consists of contract actions' do
let(:expected) do
[
['memberships/read', user.id, project.id]
]
end
end
end
end
context 'with a member without a permission and with the non member having a permission' do
let(:non_member_permissions) { %i[view_members] }
let(:members) { [member, non_member_role] }
it_behaves_like 'consists of contract actions' do
let(:expected) do
[
['memberships/read', user.id, project.id]
]
end
end
end
context 'with a member with a permission and with the non member having the same permission' do
let(:non_member_permissions) { %i[view_members] }
let(:member_permissions) { %i[view_members] }
let(:members) { [member, non_member_role] }
it_behaves_like 'consists of contract actions' do
let(:expected) do
[
['memberships/read', user.id, project.id]
]
end
end
end
context 'with the non member role with an action permission and the user being locked' do
let(:non_member_permissions) { %i[view_members] }
let(:members) { [non_member_role] }
let(:project_public) { true }
let(:user_status) { Principal.statuses[:locked] }
it_behaves_like 'is empty'
end
context 'with an admin' do
let(:user_admin) { true }
it_behaves_like 'consists of contract actions' do
let(:expected) do
# This complicated and programmatic way is chosen so that the test can deal with additional actions being defined
item = ->(namespace, action, global, module_name) {
# We only expect contract actions for project modules that are enabled by default. In the
# default edition the Bim module is not enabled by default for instance and thus it's contract
# actions are not expected to be part of the default capabilities.
return if module_name.present? && project.enabled_module_names.exclude?(module_name.to_s)
["#{API::Utilities::PropertyNameConverter.from_ar_name(namespace.to_s.singularize).pluralize.underscore}/#{action}",
user.id,
global ? nil : project.id]
}
OpenProject::AccessControl
.contract_actions_map
.map { |_, v| v[:actions].map { |vk, vv| vv.map { |vvv| item.call(vk, vvv, v[:global], v[:module]) } } }
.flatten(2)
.compact
end
end
end
context 'with an admin but with modules deactivated' do
let(:user_admin) { true }
before do
project.enabled_modules = []
end
it_behaves_like 'consists of contract actions' do
let(:expected) do
# This complicated and programmatic way is chosen so that the test can deal with additional actions being defined
item = ->(namespace, action, global, module_name) {
return if module_name.present?
["#{API::Utilities::PropertyNameConverter.from_ar_name(namespace.to_s.singularize).pluralize.underscore}/#{action}",
user.id,
global ? nil : project.id]
}
OpenProject::AccessControl
.contract_actions_map
.map { |_, v| v[:actions].map { |vk, vv| vv.map { |vvv| item.call(vk, vvv, v[:global], v[:module]) } } }
.flatten(2)
.compact
end
end
end
context 'with an admin but being locked' do
let(:user_admin) { true }
let(:user_status) { Principal.statuses[:locked] }
it_behaves_like 'is empty'
end
context 'without the current user being member in a project' do
let(:permissions) { %i[manage_members] }
let(:global_permissions) { %i[manage_user] }
let(:members) { [member, global_member] }
let(:current_user_admin) { false }
it_behaves_like 'is empty'
end
context 'with the current user being member in a project' do
let(:permissions) { %i[manage_members] }
let(:global_permissions) { %i[manage_user] }
let(:members) { [own_member, member, global_member] }
let(:current_user_admin) { false }
it_behaves_like 'consists of contract actions' do
let(:expected) do
[
['memberships/create', user.id, project.id],
['memberships/destroy', user.id, project.id],
['memberships/update', user.id, project.id],
['users/create', user.id, nil],
['users/read', user.id, nil],
['users/update', user.id, nil]
]
end
end
end
context 'with a member with an action permission and the project being archived' do
let(:permissions) { %i[manage_members] }
let(:members) { [member] }
let(:project_active) { false }
it_behaves_like 'is empty'
end
end
end