Commit 9d2f2da0 authored by Tiago Botelho's avatar Tiago Botelho

Only show approver groups that are visible to the current user through the API

parent 6c43aa6b
......@@ -105,6 +105,9 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
@source_project = @merge_request.source_project
@commits = set_commits_for_rendering(@merge_request.commits)
@commit = @merge_request.diff_head_commit
# FIXME: We have to assign a presenter to another instance variable
# due to class_name checks being made with issuable classes
@mr_presenter = @merge_request.present(current_user: current_user)
@labels = LabelsFinder.new(current_user, project_id: @project.id).execute
......
......@@ -341,6 +341,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
@target_project = @merge_request.target_project
@target_branches = @merge_request.target_project.repository.branch_names
@noteable = @merge_request
# FIXME: We have to assign a presenter to another instance variable
# due to class_name checks being made with issuable classes
@mr_presenter = @merge_request.present(current_user: current_user)
end
......
......@@ -3,13 +3,14 @@
module Emails
module MergeRequests
def new_merge_request_email(recipient_id, merge_request_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
setup_merge_request_mail(merge_request_id, recipient_id, present: true)
mail_new_thread(@merge_request, merge_request_thread_options(@merge_request.author_id, recipient_id, reason))
end
def new_mention_in_merge_request_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
setup_merge_request_mail(merge_request_id, recipient_id, present: true)
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
end
......@@ -67,7 +68,7 @@ module Emails
end
def add_merge_request_approver_email(recipient_id, merge_request_id, updated_by_user_id, reason = nil)
setup_merge_request_mail(merge_request_id, recipient_id)
setup_merge_request_mail(merge_request_id, recipient_id, present: true)
@updated_by = User.find(updated_by_user_id)
mail_answer_thread(@merge_request, merge_request_thread_options(updated_by_user_id, recipient_id, reason))
......@@ -96,11 +97,16 @@ module Emails
private
def setup_merge_request_mail(merge_request_id, recipient_id)
def setup_merge_request_mail(merge_request_id, recipient_id, present: false)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request)
if present
recipient = User.find(recipient_id)
@mr_presenter = @merge_request.present(current_user: recipient)
end
@sent_notification = SentNotification.record(@merge_request, recipient_id, reply_key)
end
......
......@@ -9,7 +9,7 @@
%p
Assignee: #{@merge_request.assignee_name}
= render_if_exists 'notify/merge_request_approvers', merge_request: @merge_request
= render_if_exists 'notify/merge_request_approvers', presenter: @mr_presenter
- if @merge_request.description
%div
......
......@@ -5,6 +5,6 @@ New Merge Request <%= @merge_request.to_reference %>
<%= merge_path_description(@merge_request, 'to') %>
Author: <%= @merge_request.author_name %>
Assignee: <%= @merge_request.assignee_name %>
<%= render_if_exists 'notify/merge_request_approvers', merge_request: @merge_request %>
<%= render_if_exists 'notify/merge_request_approvers', presenter: @mr_presenter %>
<%= @merge_request.description %>
module Approvable
include Gitlab::Utils::StrongMemoize
def requires_approve?
# keep until UI changes implemented
true
end
# A method related to approvers that is user facing
# should be moved to VisibleApprovable because
# of the fact that we use filtered versions of certain methods
# such as approver_groups and target_project in presenters
include ::VisibleApprovable
def approval_needed?
approvals_required&.nonzero?
......@@ -36,66 +35,17 @@ module Approvable
super
end
# Users in the list of approvers who have not already approved this MR.
#
def approvers_left
strong_memoize(:approvers_left) do
User.where(id: all_approvers_including_groups.map(&:id)).where.not(id: approved_by_users.select(:id))
end
end
# The list of approvers from either this MR (if they've been set on the MR) or the
# target project. Excludes the author if 'self-approval' isn't explicitly
# enabled on project settings.
# Even though this method is used in VisibleApprovable
# we do not want to encounter a scenario where there are
# no user approvers but there is one merge request group
# approver that is not visible to the current_user,
# which would make this method return false
# when it should still report as overwritten.
# To prevent this from happening, approvers_overwritten?
# makes use of the unfiltered version of approver_groups so that
# it always takes into account every approver
# available
#
# Before a merge request has been created, author will be nil, so pass the current user
# on the MR create page.
#
def overall_approvers
approvers_relation = approvers_overwritten? ? approvers : target_project.approvers
if author && !authors_can_approve?
approvers_relation = approvers_relation.where.not(user_id: author.id)
end
approvers_relation.includes(:user)
end
def overall_approver_groups
approvers_overwritten? ? approver_groups : target_project.approver_groups
end
def all_approvers_including_groups
strong_memoize(:all_approvers_including_groups) do
approvers = []
# Approvers from direct assignment
approvers << approvers_from_users
approvers << approvers_from_groups
approvers.flatten
end
end
def approvers_from_users
overall_approvers.map(&:user)
end
def approvers_from_groups
group_approvers = []
overall_approver_groups.each do |approver_group|
group_approvers << approver_group.users
end
group_approvers.flatten!
group_approvers.delete(author) unless authors_can_approve?
group_approvers
end
def approvers_overwritten?
approvers.to_a.any? || approver_groups.to_a.any?
end
......@@ -145,12 +95,4 @@ module Approvable
approver_groups.find_or_initialize_by(group_id: group_id, target_id: id)
end
end
def reset_approval_cache!
approvals(true)
approved_by_users(true)
clear_memoization(:approvers_left)
clear_memoization(:all_approvers_including_groups)
end
end
# This module may only be used by presenters or modules
# that include the Approvable concern
module VisibleApprovable
include ::Gitlab::Utils::StrongMemoize
def requires_approve?
# keep until UI changes implemented
true
end
# Users in the list of approvers who have not already approved this MR.
#
def approvers_left
strong_memoize(:approvers_left) do
User.where(id: all_approvers_including_groups.map(&:id)).where.not(id: approved_by_users.select(:id))
end
end
# The list of approvers from either this MR (if they've been set on the MR) or the
# target project. Excludes the author if 'self-approval' isn't explicitly
# enabled on project settings.
#
# Before a merge request has been created, author will be nil, so pass the current user
# on the MR create page.
#
def overall_approvers
approvers_relation = approvers_overwritten? ? approvers : target_project.approvers
if author && !authors_can_approve?
approvers_relation = approvers_relation.where.not(user_id: author.id)
end
approvers_relation.includes(:user)
end
def overall_approver_groups
approvers_overwritten? ? approver_groups : target_project.approver_groups
end
def all_approvers_including_groups
strong_memoize(:all_approvers_including_groups) do
approvers = []
# Approvers from direct assignment
approvers << approvers_from_users
approvers << approvers_from_groups
approvers.flatten
end
end
def approvers_from_users
overall_approvers.map(&:user)
end
def approvers_from_groups
group_approvers = overall_approver_groups.flat_map(&:users)
group_approvers.delete(author) unless authors_can_approve?
group_approvers
end
def reset_approval_cache!
approvals(true)
approved_by_users(true)
clear_memoization(:approvers_left)
clear_memoization(:all_approvers_including_groups)
end
end
module EE
module MergeRequestPresenter
include ::Gitlab::Utils::StrongMemoize
include ::VisibleApprovable
def approvals_path
if requires_approve?
......@@ -8,34 +8,8 @@ module EE
end
end
def all_approvers_including_groups
strong_memoize(:all_approvers_including_groups) do
approvers = []
# approvers from direct assignment
approvers << merge_request.approvers_from_users
approvers << approvers_from_groups
approvers.flatten
end
end
def overall_approver_groups
if approvers_overwritten?
approver_groups
else
merge_request.target_project.present(current_user: current_user).approver_groups
end
end
def approvers_overwritten?
merge_request.approvers.to_a.any? || approver_groups.to_a.any?
end
private
def approvers_from_groups
overall_approver_groups.flat_map(&:users) - [merge_request.author]
def target_project
merge_request.target_project.present(current_user: current_user)
end
def approver_groups
......
- merge_request = local_assigns.fetch(:merge_request)
- return unless merge_request.approvers.any?
- presenter = local_assigns.fetch(:presenter)
- return unless presenter.approvers.any?
%p
Approvers: #{render_items_list(merge_request.approvers_left.map(&:name))}
Approvers: #{render_items_list(presenter.approvers_left.map(&:name))}
<%- merge_request = local_assigns.fetch(:merge_request) -%>
<%- return unless merge_request.approvers.any? -%>
Approvers: <%= render_items_list(merge_request.approvers_left.map(&:name)) %>
<%- presenter = local_assigns.fetch(:presenter) -%>
<%- return unless presenter.approvers.any? -%>
Approvers: <%= render_items_list(presenter.approvers_left.map(&:name)) %>
......@@ -8,9 +8,7 @@
%p
Assignee: #{@merge_request.author_name} &rarr; #{@merge_request.assignee_name}
- if @merge_request.approvers.any?
%p
Approvers: #{render_items_list(@merge_request.approvers_left.map(&:name))}
= render_if_exists 'notify/merge_request_approvers', presenter: @mr_presenter
- if @merge_request.description
= markdown(@merge_request.description, pipeline: :email, author: @merge_request.author)
......@@ -5,6 +5,4 @@
<%= merge_path_description(@merge_request, 'to') %>
Author: <%= @merge_request.author_name %>
Assignee: <%= @merge_request.assignee_name %>
<% if @merge_request.approvers.any? %>
Approvers: <%= render_items_list(@merge_request.approvers_left.map(&:name)) %>
<% end %>
<%= render_if_exists 'notify/merge_request_approvers', presenter: @mr_presenter %>
......@@ -3,10 +3,9 @@
- form = local_assigns.fetch(:form)
- return unless issuable.is_a?(MergeRequest)
- return unless issuable.requires_approve?
- return unless presenter.requires_approve?
- author = issuable.author || current_user
- authors_can_approve = issuable.merge_requests_author_approval?
- can_update_approvers = can?(current_user, :update_approvers, issuable)
.form-group.row
......@@ -14,7 +13,7 @@
Approvers
.col-sm-10
- if can_update_approvers
- skip_users = [*presenter.all_approvers_including_groups, (author unless authors_can_approve)].compact
- skip_users = [*presenter.all_approvers_including_groups, (author unless presenter.authors_can_approve?)].compact
= users_select_tag("merge_request[approver_ids]",
multiple: true,
......@@ -22,6 +21,7 @@
email_user: true,
skip_users: skip_users,
project: issuable.target_project)
.form-text.text-muted
This merge request must be approved by these users.
You can override the project settings by setting your own list of approvers.
......@@ -41,7 +41,7 @@
- else
- unsaved_approvers = !presenter.approvers_overwritten?
- item_classes = unsaved_approvers ? ['unsaved-approvers'] : []
- issuable.overall_approvers.each do |approver|
- presenter.overall_approvers.each do |approver|
%li{ id: dom_id(approver.user), class: item_classes + ['approver'] }
= link_to approver.user.name, approver.user
- if can_update_approvers
......
......@@ -42,7 +42,7 @@ module API
get 'approvals' do
merge_request = find_merge_request_with_access(params[:merge_request_iid])
present merge_request, with: Entities::MergeRequestApprovals, current_user: current_user
present merge_request.present(current_user: current_user), with: Entities::MergeRequestApprovals, current_user: current_user
end
desc 'Change approval-related configuration' do
......@@ -60,7 +60,7 @@ module API
merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, approvals_before_merge: params[:approvals_required]).execute(merge_request)
if merge_request.valid?
present merge_request, with: Entities::MergeRequestApprovals, current_user: current_user
present merge_request.present(current_user: current_user), with: Entities::MergeRequestApprovals, current_user: current_user
else
handle_merge_request_errors! merge_request.errors
end
......@@ -80,7 +80,7 @@ module API
merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, declared(params, include_parent_namespaces: false).merge(remove_old_approvers: true)).execute(merge_request)
if merge_request.valid?
present merge_request, with: Entities::MergeRequestApprovals, current_user: current_user
present merge_request.present(current_user: current_user), with: Entities::MergeRequestApprovals, current_user: current_user
else
handle_merge_request_errors! merge_request.errors
end
......@@ -111,7 +111,7 @@ module API
.new(user_project, current_user)
.execute(merge_request)
present merge_request, with: Entities::MergeRequestApprovals, current_user: current_user
present merge_request.present(current_user: current_user), with: Entities::MergeRequestApprovals, current_user: current_user
end
desc 'Remove an approval from a merge request' do
......@@ -126,7 +126,7 @@ module API
.new(user_project, current_user)
.execute(merge_request)
present merge_request, with: Entities::MergeRequestApprovals, current_user: current_user
present merge_request.present(current_user: current_user), with: Entities::MergeRequestApprovals, current_user: current_user
end
end
end
......
......@@ -15,7 +15,7 @@ module API
success ::API::Entities::ApprovalSettings
end
get '/' do
present user_project, with: ::API::Entities::ApprovalSettings
present user_project.present(current_user: current_user), with: ::API::Entities::ApprovalSettings
end
desc 'Change approval-related configuration' do
......@@ -34,7 +34,7 @@ module API
result = ::Projects::UpdateService.new(user_project, current_user, project_params).execute
if result[:status] == :success
present user_project, with: ::API::Entities::ApprovalSettings
present user_project.present(current_user: current_user), with: ::API::Entities::ApprovalSettings
else
render_validation_error!(user_project)
end
......@@ -53,7 +53,7 @@ module API
result = ::Projects::UpdateService.new(user_project, current_user, declared(params, include_parent_namespaces: false).merge(remove_old_approvers: true)).execute
if result[:status] == :success
present user_project, with: ::API::Entities::ApprovalSettings
present user_project.present(current_user: current_user), with: ::API::Entities::ApprovalSettings
else
render_validation_error!(user_project)
end
......
require 'spec_helper'
describe Approvable do
let(:merge_request) { create(:merge_request, :with_approver) }
let(:merge_request) { create(:merge_request) }
describe '#approvers_left' do
it 'only queries once' do
merge_request
describe '#approvers_overwritten?' do
subject { merge_request.approvers_overwritten? }
expect(User).to receive(:where).and_call_original.once
3.times { merge_request.approvers_left }
it 'returns false when merge request has no approvers' do
is_expected.to be false
end
end
describe '#reset_approval_cache!' do
it 'clears the cache of approvers left' do
user_can_approve = merge_request.approvers_left.first
merge_request.approvals.create!(user: user_can_approve)
it 'returns true when merge request has user approver' do
create(:approver, target: merge_request)
is_expected.to be true
end
merge_request.reset_approval_cache!
it 'returns true when merge request has group approver' do
group = create(:group_with_members)
create(:approver_group, target: merge_request, group: group)
expect(merge_request.approvers_left).to be_empty
is_expected.to be true
end
end
end
require 'spec_helper'
describe VisibleApprovable do
let(:resource) { create(:merge_request, source_project: project) }
let!(:project) { create(:project, :repository) }
let!(:user) { project.creator }
describe '#requires_approve' do
subject { resource.requires_approve? }
it { is_expected.to be true }
end
describe '#approvers_left' do
let!(:private_group) { create(:group_with_members, :private) }
let!(:public_group) { create(:group_with_members) }
let!(:public_approver_group) { create(:approver_group, target: resource, group: public_group) }
let!(:private_approver_group) { create(:approver_group, target: resource, group: private_group) }
let!(:approver) { create(:approver, target: resource) }
subject { resource.approvers_left }
it 'only queries once' do
expect(User).to receive(:where).and_call_original.once
3.times { subject }
end
it 'returns all approvers left' do
resource.approvals.create!(user: approver.user)
is_expected.to match_array(public_approver_group.users + private_approver_group.users)
end
end
describe '#overall_approvers' do
let!(:project_approver) { create(:approver, target: project) }
subject { resource.overall_approvers }
it 'returns a list of all the project approvers' do
is_expected.to match_array(project_approver)
end
context 'when author is approver' do
let!(:author_approver) { create(:approver, target: project, user: resource.author) }
it 'excludes author if authors cannot approve' do
is_expected.not_to include(author_approver)
end
it 'includes author if authors are able to approve' do
project.update(merge_requests_author_approval: true)
is_expected.to include(author_approver)
end
end
context 'when approvers are overwritten' do
let!(:approver) { create(:approver, target: resource) }
it 'returns the list of all the merge request user approvers' do
is_expected.to match_array(approver)
end
end
end
describe '#overall_approver_groups' do
before do
group = create(:group_with_members)
create(:approver_group, target: project, group: group)
end
subject { resource.overall_approver_groups }
it 'returns all the project approver groups' do
is_expected.to match_array(project.approver_groups)
end
context 'when group approvers are overwritten' do
it 'returns all the merge request approver groups' do
group = create(:group_with_members)
create(:approver_group, target: resource, group: group)
is_expected.to match_array(resource.approver_groups)
end
end
end
describe '#all_approvers_including_groups' do
let!(:group) { create(:group_with_members) }
let!(:approver_group) { create(:approver_group, target: resource, group: group) }
let!(:approver) { create(:approver, target: resource) }
subject { resource.all_approvers_including_groups }
it 'only queries once' do
expect(resource).to receive(:approvers_from_users).and_call_original.once
3.times { subject }
end
it 'returns all approvers (groups and users)' do
is_expected.to match_array(approver_group.users + [approver.user])
end
end
describe '#authors_can_approve?' do
subject { resource.authors_can_approve? }
it 'returns false when merge_requests_author_approval flag is off' do
is_expected.to be false
end
it 'returns true when merge_requests_author_approval flag is turned on' do
project.update(merge_requests_author_approval: true)
is_expected.to be true
end
end
describe '#reset_approval_cache!' do
before do
create(:approver, target: resource)
end
subject { resource.reset_approval_cache! }
it 'clears the cache of approvers left' do
user_can_approve = resource.approvers_left.first
resource.approvals.create!(user: user_can_approve)
subject
expect(resource.approvers_left).to be_empty
end
it 'clears the all_approvers_including_groups cache' do
resource.all_approvers_including_groups.first.destroy!
subject
expect(resource.all_approvers_including_groups).to be_empty
end
end
end
......@@ -5,16 +5,28 @@ describe MergeRequestPresenter do
let!(:project) { create(:project, :repository) }
let!(:user) { project.creator }
describe '#all_approvers_including_groups' do
describe '#approvals_path' do
subject { described_class.new(resource, current_user: user).approvals_path }
it 'returns path' do
is_expected.to eq("/#{resource.project.full_path}/merge_requests/#{resource.iid}/approvals")
end
end
describe '#approvers_left' do
let!(:private_group) { create(:group_with_members, :private) }
let!(:public_group) { create(:group_with_members) }
let!(:public_approver_group) { create(:approver_group, target: resource, group: public_group) }
let!(:private_approver_group) { create(:approver_group, target: resource, group: private_group) }
let!(:approver) { create(:approver, target: resource) }
subject { described_class.new(resource, current_user: user).all_approvers_including_groups }
before do
resource.approvals.create!(user: approver.user)
end
subject { described_class.new(resource, current_user: user).approvers_left }
it { is_expected.to match_array([public_approver_group.users, approver.user].flatten) }
it { is_expected.to match_array(public_approver_group.users) }
context 'when user has access to private group' do
before do
......@@ -22,7 +34,7 @@ describe MergeRequestPresenter do
end
it do
approvers = [public_approver_group.users, private_approver_group.users, approver.user].flatten - [user]
approvers = public_approver_group.users + private_approver_group.users - [user]
is_expected.to match_array(approvers)
end
......@@ -30,43 +42,45 @@ describe MergeRequestPresenter do
end
describe '#overall_approver_groups' do
subject { described_class.new(resource, current_user: user).overall_approver_groups }
let!(:private_group) { create(:group_with_members, :private) }
let!(:public_group) { create(:group_with_members) }
let!(:public_approver_group) { create(:approver_group, target: resource, group: public_group) }
let!(:private_approver_group) { create(:approver_group, target: resource, group: private_group) }
context 'when approvers is overwritten' do
let!(:public_approver_group) { create(:approver_group, target: project) }
subject { described_class.new(resource, current_user: user).overall_approver_groups }
it { is_expected.to match_array(project.approver_groups) }
end
it { is_expected.to match_array([public_approver_group]) }
context 'when approvers is not overwritten' do
let!(:public_approver_group) { create(:approver_group, target: resource) }
context 'when user has access to private group' do
before do
private_group.add_user(user, Gitlab::Access::DEVELOPER)
end
it { is_expected.to match_array([public_approver_group]) }
it { is_expected.to match_array([public_approver_group, private_approver_group]) }
end
end
describe '#approvers_overwritten?' do
subject { described_class.new(resource, current_user: user).approvers_overwritten? }
it { is_expected.to be_falsey }
context 'when merge request has user and group approvers' do
let!(:public_approver_group) { create(:approver_group, target: resource) }
let!(:approver) { create(:approver, user: user, target: resource) }
describe '#all_approvers_including_groups' do
let!(:private_group) { create(:group_with_members, :private) }
let!(:public_group) { create(:group_with_members) }
let!(:public_approver_group) { create(:approver_group, target: resource, group: public_group) }
let!(:private_approver_group) { create(:approver_group, target: resource, group: private_group) }
let!(:approver) { create(:approver, target: resource) }
it { is_expected.to be_truthy }
end
subject { described_class.new(resource, current_user: user).all_approvers_including_groups }
context 'when merge request only has user approvers' do
let!(:approver) { create(:approver, user: user, target: resource) }
it { is_expected.to match_array(public_approver_group.users + [approver.user]) }
it { is_expected.to be_truthy }
end
context 'when user has access to private group' do
before do
private_group.add_user(user, Gitlab::Access::DEVELOPER)
end
context 'when merge request only has group approvers' do
let!(:public_approver_group) { create(:approver_group, target: resource) }
it do
approvers = [public_approver_group.users, private_approver_group.users, approver.user].flatten - [user]
it { is_expected.to be_truthy }
is_expected.to match_array(approvers)
end
end
end
end
......@@ -33,6 +33,29 @@ describe API::MergeRequestApprovals do
expect(json_response['approver_groups'][0]['group']['name']).to eq(group.name)
end
context 'when private group approver' do
before do
private_group = create(:group, :private)
merge_request.approver_groups.create(group: private_group)
end
it 'only shows group approvers visible to the user' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approvals", user)
expect(response).to have_gitlab_http_status(200)
expect(json_response['approver_groups']).to be_empty
end
context 'when admin' do
it 'shows all approver groups' do
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approvals", admin)
expect(response).to have_gitlab_http_status(200)
expect(json_response['approver_groups'].size).to eq(1)
end
end
end
context 'when approvers are set to zero' do
before do
project.update!(approvals_before_merge: 0)
......@@ -100,17 +123,29 @@ describe API::MergeRequestApprovals do
expect(response).to have_gitlab_http_status(422)
end
end
it 'only shows approver groups that are visible to current user' do
private_group = create(:group, :private)
merge_request.approver_groups.create(group: private_group)
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approvals", current_user), approvals_required: 5
expect(response).to have_gitlab_http_status(201)
expect(json_response['approver_groups'].size).to eq(approver_groups_count)
end
end
context 'as a project admin' do
it_behaves_like 'user allowed to override approvals required' do
let(:current_user) { user }
let(:approver_groups_count) { 0 }
end
end
context 'as a global admin' do
it_behaves_like 'user allowed to override approvals required' do
let(:current_user) { admin }
let(:approver_groups_count) { 1 }
end
end
......@@ -196,17 +231,30 @@ describe API::MergeRequestApprovals do
end
end
end
it 'only shows approver groups that are visible to current user' do
private_group = create(:group, :private)
merge_request.approver_groups.create(group: private_group)
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/approvers", current_user),
approver_ids: [approver.id], approver_group_ids: [private_group.id, approver_group.id]
expect(response).to have_gitlab_http_status(200)
expect(json_response['approver_groups'].size).to eq(approver_groups_count)
end
end
context 'as a project admin' do
it_behaves_like 'user allowed to change approvers' do
let(:current_user) { user }
let(:approver_groups_count) { 1 }
end
end
context 'as a global admin' do
it_behaves_like 'user allowed to change approvers' do
let(:current_user) { admin }
let(:approver_groups_count) { 2 }
end
end
......@@ -288,6 +336,16 @@ describe API::MergeRequestApprovals do
expect(merge_request.reload.approvals_left).to eq(2)
end
end
it 'only shows group approvers visible to the user' do
private_group = create(:group, :private)
merge_request.approver_groups.create(group: private_group)
approve
expect(response).to have_gitlab_http_status(201)
expect(json_response['approver_groups']).to be_empty
end
end
end
......@@ -302,11 +360,11 @@ describe API::MergeRequestApprovals do
project.add_developer(create(:user))
merge_request.approvals.create(user: approver)
merge_request.approvals.create(user: unapprover)
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/unapprove", unapprover)
end
it 'unapproves the merge request' do
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/unapprove", unapprover)
expect(response).to have_gitlab_http_status(201)
expect(json_response['approvals_left']).to eq(1)
usernames = json_response['approved_by'].map { |u| u['user']['username'] }
......@@ -315,6 +373,16 @@ describe API::MergeRequestApprovals do
expect(json_response['user_has_approved']).to be false
expect(json_response['user_can_approve']).to be true
end
it 'only shows group approvers visible to the user' do
private_group = create(:group, :private)
merge_request.approver_groups.create(group: private_group)
post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/unapprove", unapprover)
expect(response).to have_gitlab_http_status(201)
expect(json_response['approver_groups']).to be_empty
end
end
end
end
......@@ -27,6 +27,16 @@ describe API::ProjectApprovals do
expect(response).to match_response_schema('public_api/v4/project_approvers', dir: 'ee')
end
end
it 'only shows approver groups that are visible to the user' do
private_group = create(:group, :private)
project.approver_groups.create(group: private_group)
get api(url, user)
expect(response).to match_response_schema('public_api/v4/project_approvers', dir: 'ee')
expect(json_response["approver_groups"]).to be_empty
end
end
describe 'POST /projects/:id/approvers' do
......@@ -68,18 +78,30 @@ describe API::ProjectApprovals do
expect(JSON.parse(response.body).symbolize_keys).to include(settings)
end
it 'only shows approver groups that are visible to the current user' do
private_group = create(:group, :private)
project.approver_groups.create(group: private_group)
post api(url, current_user), approvals_before_merge: 3
expect(response).to match_response_schema('public_api/v4/project_approvers', dir: 'ee')
expect(json_response["approver_groups"].size).to eq(visible_approver_groups_count)
end
end
end
context 'as a project admin' do
it_behaves_like 'a user with access' do
let(:current_user) { user }
let(:visible_approver_groups_count) { 0 }
end
end
context 'as a global admin' do
it_behaves_like 'a user with access' do
let(:current_user) { admin }
let(:visible_approver_groups_count) { 1 }
end
end
......@@ -94,6 +116,7 @@ describe API::ProjectApprovals do
describe 'PUT /projects/:id/approvers' do
let(:url) { "/projects/#{project.id}/approvers" }
shared_examples_for 'a user with access' do
it 'removes all approvers if no params are given' do
project.approvers.create(user: approver)
......@@ -136,17 +159,31 @@ describe API::ProjectApprovals do
expect(json_response['approvers'][0]['user']['username']).to eq(approver.username)
expect(json_response['approver_groups'][0]['group']['name']).to eq(group.name)
end
it 'only shows approver groups that are visible to the current user' do
private_group = create(:group, :private)
project.approvers.create(user: approver)
expect do
put api(url, current_user), approver_ids: [approver.id], approver_group_ids: [private_group.id]
end.to change { project.approver_groups.count }.from(0).to(1)
expect(response).to match_response_schema('public_api/v4/project_approvers', dir: 'ee')
expect(json_response["approver_groups"].size).to eq(visible_approver_groups_count)
end
end
context 'as a project admin' do
it_behaves_like 'a user with access' do
let(:current_user) { user }
let(:visible_approver_groups_count) { 0 }
end
end
context 'as a global admin' do
it_behaves_like 'a user with access' do
let(:current_user) { admin }
let(:visible_approver_groups_count) { 1 }
end
end
......
......@@ -434,33 +434,6 @@ describe MergeRequestPresenter do
end
end
describe '#approvals_path' do
before do
allow(resource).to receive(:requires_approve?) { requires_approve }
end
subject do
described_class.new(resource, current_user: user).approvals_path
end
context 'when approvals required' do
let(:requires_approve) { true }
it 'returns path' do
is_expected
.to eq("/#{resource.project.full_path}/merge_requests/#{resource.iid}/approvals")
end
end
context 'when approvals not required' do
let(:requires_approve) { false }
it 'returns nil' do
is_expected.to be_nil
end
end
end
describe '#rebase_path' do
before do
allow(resource).to receive(:rebase_in_progress?) { rebase_in_progress }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment