mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
484 lines
15 KiB
Ruby
484 lines
15 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
#-- copyright
|
|
# OpenProject is an open source project management software.
|
|
# Copyright (C) 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 COPYRIGHT and LICENSE files for more details.
|
|
#++
|
|
|
|
require "spec_helper"
|
|
|
|
RSpec.describe MembersController do
|
|
shared_let(:admin) { create(:admin) }
|
|
shared_let(:project) { create(:project, identifier: "pet_project") }
|
|
|
|
let(:user) { create(:user) }
|
|
let(:role) { create(:project_role) }
|
|
let(:member) do
|
|
create(:member, project:,
|
|
user:,
|
|
roles: [role])
|
|
end
|
|
|
|
before { login_as(admin) }
|
|
|
|
describe "#autocomplete_for_member" do
|
|
let(:params) { { "project_id" => project.identifier.to_s, "q" => query } }
|
|
let(:query) { "" }
|
|
let(:json_response) { response.parsed_body }
|
|
let(:global_permissions) { [] }
|
|
let(:project_permissions) { [] }
|
|
|
|
let(:user) do
|
|
create(:user,
|
|
member_with_permissions: { project => project_permissions },
|
|
global_permissions: global_permissions)
|
|
end
|
|
|
|
subject { post(:autocomplete_for_member, xhr: true, params:, format: :json) }
|
|
|
|
before do
|
|
login_as(user)
|
|
end
|
|
|
|
describe "WHEN the user is authorized to view all users WHEN a project is provided" do
|
|
let(:project_permissions) { [:manage_members] }
|
|
let(:global_permissions) { [:view_all_principals] }
|
|
|
|
it "is success" do
|
|
subject
|
|
expect(response).to be_successful
|
|
end
|
|
|
|
context "when the user is not authorized to see email addresses" do
|
|
it "returns id, name and href" do
|
|
subject
|
|
expect(json_response).to be_an(Array)
|
|
expect(json_response).to include(
|
|
{
|
|
"id" => admin.id,
|
|
"name" => admin.name,
|
|
"href" => "/api/v3/users/#{admin.id}"
|
|
}
|
|
)
|
|
end
|
|
|
|
context "when searching email addresses" do
|
|
let(:query) { admin.mail }
|
|
|
|
it "does not return matches from emails" do
|
|
subject
|
|
expect(json_response).to be_empty
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when the user is authorized to see email addresses" do
|
|
let(:global_permissions) { %i[view_all_principals view_user_email] }
|
|
|
|
it "returns id, name, email and href" do
|
|
subject
|
|
expect(json_response).to be_an(Array)
|
|
expect(json_response).to include(
|
|
{
|
|
"id" => admin.id,
|
|
"name" => admin.name,
|
|
"email" => admin.mail,
|
|
"href" => "/api/v3/users/#{admin.id}"
|
|
}
|
|
)
|
|
end
|
|
|
|
context "when searching email addresses" do
|
|
let(:query) { admin.mail }
|
|
|
|
it "returns matches from emails" do
|
|
subject
|
|
expect(json_response).to include(
|
|
{
|
|
"id" => admin.id,
|
|
"name" => admin.name,
|
|
"email" => admin.mail,
|
|
"href" => "/api/v3/users/#{admin.id}"
|
|
}
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "WHEN the user has manage_members but no view_all_users (reduced visibility)" do
|
|
let(:project_permissions) { [:manage_members] }
|
|
let!(:other_project) { create(:project) }
|
|
let!(:other_user) { create(:user, member_with_permissions: { other_project => %i[view_project] }) }
|
|
let!(:user) do
|
|
create(:user,
|
|
global_permissions: global_permissions,
|
|
member_with_permissions: { project => project_permissions, other_project => %i[view_project] })
|
|
end
|
|
|
|
context "when the user is not authorized to see email addresses" do
|
|
it "returns only users visible through shared projects" do
|
|
subject
|
|
expect(json_response).to be_an(Array)
|
|
expect(json_response).to include(
|
|
{
|
|
"id" => other_user.id,
|
|
"name" => other_user.name,
|
|
"href" => "/api/v3/users/#{other_user.id}"
|
|
}
|
|
)
|
|
expect(json_response).not_to include(
|
|
{
|
|
"id" => admin.id,
|
|
"name" => admin.name,
|
|
"href" => "/api/v3/users/#{admin.id}"
|
|
}
|
|
)
|
|
end
|
|
|
|
context "when searching email addresses" do
|
|
let(:query) { other_user.mail }
|
|
|
|
it "does not return matches from emails" do
|
|
subject
|
|
expect(json_response).to be_empty
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when the user is authorized to see email addresses" do
|
|
let(:global_permissions) { [:view_user_email] }
|
|
|
|
it "returns id, name, email and href for visible users" do
|
|
subject
|
|
expect(json_response).to be_an(Array)
|
|
expect(json_response).to include(
|
|
{
|
|
"id" => other_user.id,
|
|
"name" => other_user.name,
|
|
"email" => other_user.mail,
|
|
"href" => "/api/v3/users/#{other_user.id}"
|
|
}
|
|
)
|
|
end
|
|
|
|
context "when searching email addresses" do
|
|
let(:query) { other_user.mail }
|
|
|
|
it "returns matches from emails for visible users" do
|
|
subject
|
|
expect(json_response).to include(
|
|
{
|
|
"id" => other_user.id,
|
|
"name" => other_user.name,
|
|
"email" => other_user.mail,
|
|
"href" => "/api/v3/users/#{other_user.id}"
|
|
}
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "WHEN the user is not authorized" do
|
|
it "is forbidden" do
|
|
subject
|
|
expect(response).to have_http_status(:forbidden)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#index" do
|
|
let(:role) { create(:project_role, permissions: [:manage_members]) }
|
|
let!(:member) { create(:member, project:, user:, roles: [role]) }
|
|
|
|
let!(:visible_group) { create(:group, members: [user]) }
|
|
let!(:hidden_group) { create(:group) }
|
|
|
|
before { login_as(user) }
|
|
|
|
it "only includes groups the user is a member of in the filter options" do
|
|
get :index, params: { project_id: project.id }
|
|
|
|
expect(response).to be_successful
|
|
|
|
groups = assigns(:members_filter_options)[:groups]
|
|
expect(groups).to include(visible_group)
|
|
expect(groups).not_to include(hidden_group)
|
|
end
|
|
end
|
|
|
|
describe "#create with reduced visibility" do
|
|
let(:project_permissions) { %i[manage_members invite_members_by_email] }
|
|
let!(:other_project) { create(:project) }
|
|
let!(:other_user) { create(:user, member_with_permissions: { other_project => %i[view_project] }) }
|
|
let!(:user) do
|
|
create(:user, member_with_permissions: { project => project_permissions, other_project => %i[view_project] })
|
|
end
|
|
|
|
before do
|
|
login_as(user)
|
|
end
|
|
|
|
context "when inviting by email an existing user who is not visible" do
|
|
let!(:hidden_user) { create(:user, mail: "hidden@example.com") }
|
|
let(:params) do
|
|
{
|
|
project_id: project.id,
|
|
member: {
|
|
role_ids: [role.id],
|
|
user_ids: [hidden_user.mail]
|
|
}
|
|
}
|
|
end
|
|
|
|
it "adds the existing user as a member instead of creating a new invitation" do
|
|
expect { post :create, params: }
|
|
.to change(Member, :count).by(1)
|
|
.and change(User, :count).by(0)
|
|
|
|
expect(response).to redirect_to "/projects/pet_project/members?status=all"
|
|
|
|
# The hidden user should now be a member of the project
|
|
hidden_user.reload
|
|
expect(hidden_user).to be_member_of(project)
|
|
|
|
# No invitation email should be sent since the user already exists
|
|
expect(ActionMailer::Base.deliveries).to be_empty
|
|
end
|
|
end
|
|
|
|
context "when adding by direct user ID a user who is not visible" do
|
|
let!(:hidden_user) { create(:user) }
|
|
let(:params) do
|
|
{
|
|
project_id: project.id,
|
|
member: {
|
|
role_ids: [role.id],
|
|
user_ids: [hidden_user.id]
|
|
}
|
|
}
|
|
end
|
|
|
|
it "does not add the hidden user as a member" do
|
|
expect { post :create, params: }
|
|
.to change(Member, :count).by(0)
|
|
|
|
hidden_user.reload
|
|
expect(hidden_user).not_to be_member_of(project)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#create" do
|
|
render_views
|
|
let(:user2) { create(:user) }
|
|
let(:user3) { create(:user) }
|
|
let(:user4) { create(:user) }
|
|
|
|
context "for single member" do
|
|
let(:action) do
|
|
post :create,
|
|
params: {
|
|
project_id: project.id,
|
|
member: { role_ids: [role.id], user_id: user2.id }
|
|
}
|
|
end
|
|
|
|
it "adds a member" do
|
|
expect { action }.to change(Member, :count).by(1)
|
|
expect(response).to redirect_to "/projects/pet_project/members?status=all"
|
|
expect(user2).to be_member_of(project)
|
|
end
|
|
end
|
|
|
|
context "for multiple members" do
|
|
let(:action) do
|
|
post :create,
|
|
params: {
|
|
project_id: project.id,
|
|
member: { role_ids: [role.id], user_ids: [user2.id, user3.id, user4.id] }
|
|
}
|
|
end
|
|
|
|
it "adds all members" do
|
|
expect { action }.to change(Member, :count).by(3)
|
|
expect(response).to redirect_to "/projects/pet_project/members?status=all"
|
|
expect(user2).to be_member_of(project)
|
|
expect(user3).to be_member_of(project)
|
|
expect(user4).to be_member_of(project)
|
|
end
|
|
end
|
|
|
|
context "with yet-to-be-invited emails" do
|
|
let(:emails) { ["h.wurst@openproject.com", "1277551@openproject.com"] }
|
|
let(:params) do
|
|
{
|
|
project_id: project.id,
|
|
member: {
|
|
role_ids: [role.id],
|
|
user_ids: [emails.first] + [user2.id, user3.id] + [emails.last]
|
|
}
|
|
}
|
|
end
|
|
|
|
let(:invited_users) { User.where(mail: emails).to_a }
|
|
let(:users) { invited_users + [user2, user3] }
|
|
let(:original_member_count) { Member.count }
|
|
|
|
before do
|
|
original_member_count
|
|
|
|
perform_enqueued_jobs do
|
|
post :create, params:
|
|
end
|
|
end
|
|
|
|
it "redirects to the members list" do
|
|
expect(response).to redirect_to "/projects/pet_project/members?status=all"
|
|
end
|
|
|
|
it "adds members" do
|
|
expect(users.size).to eq 4 # 2 emails, 2 existing users
|
|
expect(users).to all be_member_of(project)
|
|
|
|
expect(Member.count).to eq (original_member_count + users.size)
|
|
end
|
|
|
|
it "invites new users" do
|
|
mails = ActionMailer::Base.deliveries
|
|
|
|
expect(invited_users.size).to eq 2
|
|
expect(mails.size).to eq invited_users.size
|
|
expect(mails.map(&:to).flatten).to eq invited_users.map(&:mail)
|
|
|
|
mails.each do |mail|
|
|
expect(mail.subject).to include "account activation"
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with a failed save" do
|
|
let(:invalid_params) do
|
|
{ project_id: project.id,
|
|
member: { role_ids: [],
|
|
user_ids: [user2.id, user3.id, user4.id] } }
|
|
end
|
|
|
|
before do
|
|
post :create, params: invalid_params
|
|
end
|
|
|
|
it "does not redirect to the members index" do
|
|
expect(response).not_to redirect_to "/projects/pet_project/members"
|
|
end
|
|
|
|
it "shows an error message" do
|
|
expect(response.body).to include "Roles need to be assigned."
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#destroy_by_principal" do
|
|
let(:action) do
|
|
delete :destroy_by_principal, params: { project_id: project.id, principal_id: user.id, **more_params }
|
|
end
|
|
|
|
let(:role) { create(:project_role, permissions: %i[manage_members share_work_packages]) }
|
|
let!(:project_role_member) do
|
|
create(:member, project:,
|
|
user:,
|
|
roles: [role])
|
|
end
|
|
|
|
let(:work_package_role) { create(:view_work_package_role) }
|
|
let!(:work_packages_shares) do
|
|
Array.new(2) do
|
|
create(:member,
|
|
project:,
|
|
roles: [work_package_role],
|
|
entity: create(:work_package, project:),
|
|
principal: user)
|
|
end
|
|
end
|
|
|
|
before do
|
|
allow(User).to receive(:current).and_return(user)
|
|
end
|
|
|
|
context "when requested to delete only project role member" do
|
|
let(:more_params) { { project: "✓" } }
|
|
|
|
it "destroys the project role member" do
|
|
expect { action }.to change(Member, :count).by(-1)
|
|
expect(response).to redirect_to "/projects/pet_project/members"
|
|
expect(user).not_to be_member_of(project)
|
|
end
|
|
end
|
|
|
|
context "when requested to delete only work packages shares" do
|
|
let(:more_params) { { work_package_shares_role_id: "all" } }
|
|
|
|
it "destroys the project role member" do
|
|
expect { action }.to change(Member, :count).by(-2)
|
|
expect(response).to redirect_to "/projects/pet_project/members"
|
|
expect(user).to be_member_of(project)
|
|
end
|
|
end
|
|
|
|
context "when requested to delete both project role member and work packages shares" do
|
|
let(:more_params) { { project: "✓", work_package_shares_role_id: "all" } }
|
|
|
|
it "destroys the project role member" do
|
|
expect { action }.to change(Member, :count).by(-3)
|
|
expect(response).to redirect_to "/projects/pet_project/members"
|
|
expect(user).not_to be_member_of(project)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#update" do
|
|
let(:action) do
|
|
post :update,
|
|
params: {
|
|
project_id: project.id,
|
|
id: member.id,
|
|
member: { role_ids: [role2.id], user_id: user.id }
|
|
}
|
|
end
|
|
let(:role2) { create(:project_role) }
|
|
|
|
before do
|
|
member
|
|
end
|
|
|
|
it "updates the member" do
|
|
expect { action }.not_to change(Member, :count)
|
|
expect(response).to redirect_to "/projects/pet_project/members"
|
|
end
|
|
end
|
|
end
|