diff --git a/debian/patches/gitlab-v12.5.4..v12.5.6.diff b/debian/patches/gitlab-v12.5.4..v12.5.6.diff new file mode 100644 index 0000000000..9bd4180621 --- /dev/null +++ b/debian/patches/gitlab-v12.5.4..v12.5.6.diff @@ -0,0 +1,715 @@ +--- a/CHANGELOG-EE.md ++++ b/CHANGELOG-EE.md +@@ -1,5 +1,16 @@ + Please view this file on the master branch, on stable branches it's out of date. + ++## 12.5.5 ++ ++- No changes. ++ ++## 12.5.4 ++ ++### Security (1 change) ++ ++- Fix stale Elasticsearch permissions when moving group from public group to private parent group. ++ ++ + ## 12.5.3 + + ### Performance (1 change) +--- a/CHANGELOG.md ++++ b/CHANGELOG.md +@@ -2,9 +2,35 @@ + documentation](doc/development/changelog.md) for instructions on adding your own + entry. + ++## 12.5.6 ++ ++### Security (5 changes) ++ ++- GraphQL: Add timeout to all queries. ++- Return only runners from groups where user is owner for user CI owned runners. ++- Filter out notification settings for projects that a user does not have at least read access. ++- Hide project name and path when unsusbcribing from an issue or merge request. ++- Fix 500 error caused by invalid byte sequences in uploads links. ++ ++ ++## 12.5.5 ++ ++### Security (1 change) ++ ++- Upgrade Akismet gem to v3.0.0. !21786 ++ ++### Fixed (2 changes) ++ ++- Fix error in updating runner session. !20902 ++- Fix Asana integration. !21501 ++ ++ + ## 12.5.4 + +-- No changes. ++### Security (1 change) ++ ++- Update maven_file_name_regex for full string match. ++ + + ## 12.5.3 + +--- a/Gemfile ++++ b/Gemfile +@@ -50,7 +50,7 @@ + + # Spam and anti-bot protection + gem 'recaptcha', '~> 4.11', require: 'recaptcha/rails' +-gem 'akismet', '~> 2.0' ++gem 'akismet', '~> 3.0' + gem 'invisible_captcha', '~> 0.12.1' + + # Two-factor authentication +@@ -231,7 +231,7 @@ + gem 'hangouts-chat', '~> 0.0.5' + + # Asana integration +-gem 'asana', '~> 0.8.1' ++gem 'asana', '~> 0.9' + + # FogBugz integration + gem 'ruby-fogbugz', '~> 0.2.1' +--- a/Gemfile.lock ++++ b/Gemfile.lock +@@ -58,16 +58,16 @@ + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) + aes_key_wrap (1.0.1) +- akismet (2.0.0) ++ akismet (3.0.0) + apollo_upload_server (2.0.0.beta.3) + graphql (>= 1.8) + rails (>= 4.2) + arel (9.0.0) +- asana (0.8.1) ++ asana (0.9.3) + faraday (~> 0.9) + faraday_middleware (~> 0.9) + faraday_middleware-multi_json (~> 0.0) +- oauth2 (~> 1.0) ++ oauth2 (~> 1.4) + asciidoctor (2.0.10) + asciidoctor-include-ext (0.3.1) + asciidoctor (>= 1.5.6, < 3.0.0) +@@ -1117,9 +1117,9 @@ + activerecord-explain-analyze (~> 0.1) + acts-as-taggable-on (~> 6.0) + addressable (~> 2.5.2) +- akismet (~> 2.0) ++ akismet (~> 3.0) + apollo_upload_server (~> 2.0.0.beta3) +- asana (~> 0.8.1) ++ asana (~> 0.9) + asciidoctor (~> 2.0.10) + asciidoctor-include-ext (~> 0.3.1) + asciidoctor-plantuml (= 0.0.10) +--- a/VERSION ++++ b/VERSION +@@ -1 +1 @@ +-12.5.4 ++12.5.6 +--- a/app/controllers/profiles/notifications_controller.rb ++++ b/app/controllers/profiles/notifications_controller.rb +@@ -11,6 +11,7 @@ + exclude_group_ids: @group_notifications.select(:source_id) + ).execute.map { |group| current_user.notification_settings_for(group, inherit: true) } + @project_notifications = current_user.notification_settings.for_projects.order(:id) ++ .select { |notification| current_user.can?(:read_project, notification.source) } + @global_notification_setting = current_user.global_notification_setting + end + # rubocop: enable CodeReuse/ActiveRecord +--- a/app/helpers/notifications_helper.rb ++++ b/app/helpers/notifications_helper.rb +@@ -116,4 +116,8 @@ + def show_unsubscribe_title?(noteable) + can?(current_user, "read_#{noteable.to_ability_name}".to_sym, noteable) + end ++ ++ def can_read_project?(project) ++ can?(current_user, :read_project, project) ++ end + end +--- a/app/models/ci/build.rb ++++ b/app/models/ci/build.rb +@@ -53,7 +53,7 @@ + + has_one :runner_session, class_name: 'Ci::BuildRunnerSession', validate: true, inverse_of: :build + +- accepts_nested_attributes_for :runner_session ++ accepts_nested_attributes_for :runner_session, update_only: true + accepts_nested_attributes_for :job_variables + + delegate :url, to: :runner_session, prefix: true, allow_nil: true +--- a/app/models/project_services/asana_service.rb ++++ b/app/models/project_services/asana_service.rb +@@ -81,12 +81,12 @@ + def check_commit(message, push_msg) + # matches either: + # - #1234 +- # - https://app.asana.com/0/0/1234 ++ # - https://app.asana.com/0/{project_gid}/{task_gid} + # optionally preceded with: + # - fix/ed/es/ing + # - close/s/d + # - closing +- issue_finder = %r{(fix\w*|clos[ei]\w*+)?\W*(?:https://app\.asana\.com/\d+/\d+/(\d+)|#(\d+))}i ++ issue_finder = %r{(fix\w*|clos[ei]\w*+)?\W*(?:https://app\.asana\.com/\d+/\w+/(\w+)|#(\w+))}i + + message.scan(issue_finder).each do |tuple| + # tuple will be +@@ -94,7 +94,7 @@ + taskid = tuple[2] || tuple[1] + + begin +- task = Asana::Task.find_by_id(client, taskid) ++ task = Asana::Resources::Task.find_by_id(client, taskid) + task.add_comment(text: "#{push_msg} #{message}") + + if tuple[0] +--- a/app/models/user.rb ++++ b/app/models/user.rb +@@ -1307,7 +1307,7 @@ + .select('ci_runners.*') + + group_runners = Ci::RunnerNamespace +- .where(namespace_id: owned_or_maintainers_groups.select(:id)) ++ .where(namespace_id: owned_groups.select(:id)) + .joins(:runner) + .select('ci_runners.*') + +--- a/app/views/sent_notifications/unsubscribe.html.haml ++++ b/app/views/sent_notifications/unsubscribe.html.haml +@@ -1,13 +1,16 @@ + - noteable = @sent_notification.noteable + - noteable_type = @sent_notification.noteable_type.titleize.downcase + - noteable_text = show_unsubscribe_title?(noteable) ? %(#{noteable.title} (#{noteable.to_reference})) : %(#{noteable.to_reference}) +-- page_title _("Unsubscribe"), noteable_text, noteable_type.pluralize, @sent_notification.project.full_name ++- show_project_path = can_read_project?(@sent_notification.project) ++- project_path = show_project_path ? @sent_notification.project.full_name : _("GitLab / Unsubscribe") ++- noteable_url = show_project_path ? url_for([@sent_notification.project.namespace.becomes(Namespace), @sent_notification.project, noteable]) : breadcrumb_title_link ++- page_title _('Unsubscribe'), noteable_text, noteable_type.pluralize, project_path + + %h3.page-title + = _("Unsubscribe from %{type}") % { type: noteable_type } + + %p +- - link_to_noteable_text = link_to(noteable_text, url_for([@sent_notification.project.namespace.becomes(Namespace), @sent_notification.project, noteable])) ++ - link_to_noteable_text = link_to(noteable_text, noteable_url) + = _("Are you sure you want to unsubscribe from the %{type}: %{link_to_noteable_text}?").html_safe % { type: noteable_type, link_to_noteable_text: link_to_noteable_text } + + %p +--- a/config/initializers/graphql.rb ++++ b/config/initializers/graphql.rb +@@ -5,3 +5,7 @@ + + GraphQL::Schema::Object.accepts_definition(:authorize) + GraphQL::Schema::Field.accepts_definition(:authorize) ++ ++GitlabSchema.middleware << GraphQL::Schema::TimeoutMiddleware.new(max_seconds: ENV.fetch('GITLAB_RAILS_GRAPHQL_TIMEOUT', 30).to_i) do |timeout_error, query| ++ Gitlab::GraphqlLogger.error(message: timeout_error.to_s, query: query.query_string, query_variables: query.provided_variables) ++end +--- a/lib/banzai/filter/relative_link_filter.rb ++++ b/lib/banzai/filter/relative_link_filter.rb +@@ -116,7 +116,7 @@ + end + + def process_link_to_upload_attr(html_attr) +- path_parts = [Addressable::URI.unescape(html_attr.value)] ++ path_parts = [unescape_and_scrub_uri(html_attr.value)] + + if project + path_parts.unshift(relative_url_root, project.full_path) +@@ -172,7 +172,7 @@ + end + + def cleaned_file_path(uri) +- Addressable::URI.unescape(uri.path).scrub.delete("\0").chomp("/") ++ unescape_and_scrub_uri(uri.path).delete("\0").chomp("/") + end + + def relative_file_path(uri) +@@ -184,7 +184,7 @@ + def request_path + return unless context[:requested_path] + +- Addressable::URI.unescape(context[:requested_path]).chomp("/") ++ unescape_and_scrub_uri(context[:requested_path]).chomp("/") + end + + # Convert a relative path into its correct location based on the currently +@@ -266,6 +266,12 @@ + def repository + @repository ||= project&.repository + end ++ ++ private ++ ++ def unescape_and_scrub_uri(uri) ++ Addressable::URI.unescape(uri).scrub ++ end + end + end + end +--- a/locale/gitlab.pot ++++ b/locale/gitlab.pot +@@ -8193,6 +8193,9 @@ + msgid "GitHub import" + msgstr "" + ++msgid "GitLab / Unsubscribe" ++msgstr "" ++ + msgid "GitLab CI Linter has been moved" + msgstr "" + +--- a/qa/Dockerfile ++++ b/qa/Dockerfile +@@ -11,7 +11,7 @@ + # Update APT sources and install some dependencies + # + RUN sed -i "s/httpredir.debian.org/ftp.us.debian.org/" /etc/apt/sources.list +-RUN apt-get update && apt-get install -y wget unzip xvfb ++RUN apt-get update && apt-get install -y wget unzip xvfb lsb-release + + ## + # Install some packages from backports +--- a/spec/controllers/profiles/notifications_controller_spec.rb ++++ b/spec/controllers/profiles/notifications_controller_spec.rb +@@ -52,6 +52,35 @@ + end.to exceed_query_limit(control) + end + end ++ ++ context 'with project notifications' do ++ let!(:notification_setting) { create(:notification_setting, source: project, user: user, level: :watch) } ++ ++ before do ++ sign_in(user) ++ get :show ++ end ++ ++ context 'when project is public' do ++ let(:project) { create(:project, :public) } ++ ++ it 'shows notification setting for project' do ++ expect(assigns(:project_notifications).map(&:source_id)).to include(project.id) ++ end ++ end ++ ++ context 'when project is public' do ++ let(:project) { create(:project, :private) } ++ ++ it 'shows notification setting for project' do ++ # notification settings for given project were created before project was set to private ++ expect(user.notification_settings.for_projects.map(&:source_id)).to include(project.id) ++ ++ # check that notification settings for project where user does not have access are filtered ++ expect(assigns(:project_notifications)).to be_empty ++ end ++ end ++ end + end + + describe 'POST update' do +--- a/spec/controllers/sent_notifications_controller_spec.rb ++++ b/spec/controllers/sent_notifications_controller_spec.rb +@@ -56,7 +56,7 @@ + get(:unsubscribe, params: { id: sent_notification.reply_key }) + end + +- shared_examples 'unsubscribing as anonymous' do ++ shared_examples 'unsubscribing as anonymous' do |project_visibility| + it 'does not unsubscribe the user' do + expect(noteable.subscribed?(user, target_project)).to be_truthy + end +@@ -69,6 +69,18 @@ + expect(response.status).to eq(200) + expect(response).to render_template :unsubscribe + end ++ ++ if project_visibility == :private ++ it 'does not show project name or path' do ++ expect(response.body).not_to include(noteable.project.name) ++ expect(response.body).not_to include(noteable.project.full_name) ++ end ++ else ++ it 'shows project name or path' do ++ expect(response.body).to include(noteable.project.name) ++ expect(response.body).to include(noteable.project.full_name) ++ end ++ end + end + + context 'when project is public' do +@@ -79,7 +91,7 @@ + expect(response.body).to include(issue.title) + end + +- it_behaves_like 'unsubscribing as anonymous' ++ it_behaves_like 'unsubscribing as anonymous', :public + end + + context 'when unsubscribing from confidential issue' do +@@ -90,7 +102,7 @@ + expect(response.body).to include(confidential_issue.to_reference) + end + +- it_behaves_like 'unsubscribing as anonymous' ++ it_behaves_like 'unsubscribing as anonymous', :public + end + + context 'when unsubscribing from merge request' do +@@ -100,7 +112,12 @@ + expect(response.body).to include(merge_request.title) + end + +- it_behaves_like 'unsubscribing as anonymous' ++ it 'shows project name or path' do ++ expect(response.body).to include(issue.project.name) ++ expect(response.body).to include(issue.project.full_name) ++ end ++ ++ it_behaves_like 'unsubscribing as anonymous', :public + end + end + +@@ -110,11 +127,11 @@ + context 'when unsubscribing from issue' do + let(:noteable) { issue } + +- it 'shows issue title' do ++ it 'does not show issue title' do + expect(response.body).not_to include(issue.title) + end + +- it_behaves_like 'unsubscribing as anonymous' ++ it_behaves_like 'unsubscribing as anonymous', :private + end + + context 'when unsubscribing from confidential issue' do +@@ -125,17 +142,17 @@ + expect(response.body).to include(confidential_issue.to_reference) + end + +- it_behaves_like 'unsubscribing as anonymous' ++ it_behaves_like 'unsubscribing as anonymous', :private + end + + context 'when unsubscribing from merge request' do + let(:noteable) { merge_request } + +- it 'shows merge request title' do ++ it 'dos not show merge request title' do + expect(response.body).not_to include(merge_request.title) + end + +- it_behaves_like 'unsubscribing as anonymous' ++ it_behaves_like 'unsubscribing as anonymous', :private + end + end + end +--- a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb ++++ b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb +@@ -139,6 +139,10 @@ + # Making sure it's not a Front-end cache. + visit(diffs_project_merge_request_path(project, merge_request)) + ++ page.within '.line-resolve-all-container' do ++ page.find('.discussion-next-btn').click ++ end ++ + expect_appliable_suggestions(2) + + page.within("[id='#{hash}']") do +--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb ++++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb +@@ -124,6 +124,15 @@ + expect { filter(act) }.not_to raise_error + end + ++ it 'does not raise an exception on URIs containing invalid utf-8 byte sequences in uploads' do ++ act = link("/uploads/%FF") ++ expect { filter(act) }.not_to raise_error ++ end ++ ++ it 'does not raise an exception on URIs containing invalid utf-8 byte sequences in context requested path' do ++ expect { filter(link("files/test.md"), requested_path: '%FF') }.not_to raise_error ++ end ++ + it 'does not raise an exception with a garbled path' do + act = link("open(/var/tmp/):%20/location%0Afrom:%20/test") + expect { filter(act) }.not_to raise_error +--- a/spec/lib/gitlab/gitaly_client/cleanup_service_spec.rb ++++ b/spec/lib/gitlab/gitaly_client/cleanup_service_spec.rb +@@ -9,6 +9,10 @@ + let(:client) { described_class.new(project.repository) } + + describe '#apply_bfg_object_map_stream' do ++ before do ++ ::Gitlab::GitalyClient.clear_stubs! ++ end ++ + it 'sends an apply_bfg_object_map_stream message' do + expect_any_instance_of(Gitaly::CleanupService::Stub) + .to receive(:apply_bfg_object_map_stream) +--- a/spec/models/ci/build_runner_session_spec.rb ++++ b/spec/models/ci/build_runner_session_spec.rb +@@ -4,6 +4,7 @@ + + describe Ci::BuildRunnerSession, model: true do + let!(:build) { create(:ci_build, :with_runner_session) } ++ let(:url) { 'https://new.example.com' } + + subject { build.runner_session } + +@@ -12,6 +13,25 @@ + it { is_expected.to validate_presence_of(:build) } + it { is_expected.to validate_presence_of(:url).with_message('must be a valid URL') } + ++ context 'nested attribute assignment' do ++ it 'creates a new session' do ++ simple_build = create(:ci_build) ++ simple_build.runner_session_attributes = { url: url } ++ simple_build.save! ++ ++ session = simple_build.reload.runner_session ++ expect(session).to be_a(Ci::BuildRunnerSession) ++ expect(session.url).to eq(url) ++ end ++ ++ it 'updates session with new attributes' do ++ build.runner_session_attributes = { url: url } ++ build.save! ++ ++ expect(build.reload.runner_session.url).to eq(url) ++ end ++ end ++ + describe '#terminal_specification' do + let(:specification) { subject.terminal_specification } + +--- a/spec/models/project_services/asana_service_spec.rb ++++ b/spec/models/project_services/asana_service_spec.rb +@@ -21,6 +21,7 @@ + describe 'Execute' do + let(:user) { create(:user) } + let(:project) { create(:project) } ++ let(:gid) { "123456789ABCD" } + + def create_data_for_commits(*messages) + { +@@ -48,32 +49,32 @@ + end + + it 'calls Asana service to create a story' do +- data = create_data_for_commits('Message from commit. related to #123456') ++ data = create_data_for_commits("Message from commit. related to ##{gid}") + expected_message = "#{data[:user_name]} pushed to branch #{data[:ref]} of #{project.full_name} ( #{data[:commits][0][:url]} ): #{data[:commits][0][:message]}" + +- d1 = double('Asana::Task') ++ d1 = double('Asana::Resources::Task') + expect(d1).to receive(:add_comment).with(text: expected_message) +- expect(Asana::Task).to receive(:find_by_id).with(anything, '123456').once.and_return(d1) ++ expect(Asana::Resources::Task).to receive(:find_by_id).with(anything, gid).once.and_return(d1) + + @asana.execute(data) + end + + it 'calls Asana service to create a story and close a task' do + data = create_data_for_commits('fix #456789') +- d1 = double('Asana::Task') ++ d1 = double('Asana::Resources::Task') + expect(d1).to receive(:add_comment) + expect(d1).to receive(:update).with(completed: true) +- expect(Asana::Task).to receive(:find_by_id).with(anything, '456789').once.and_return(d1) ++ expect(Asana::Resources::Task).to receive(:find_by_id).with(anything, '456789').once.and_return(d1) + + @asana.execute(data) + end + + it 'is able to close via url' do + data = create_data_for_commits('closes https://app.asana.com/19292/956299/42') +- d1 = double('Asana::Task') ++ d1 = double('Asana::Resources::Task') + expect(d1).to receive(:add_comment) + expect(d1).to receive(:update).with(completed: true) +- expect(Asana::Task).to receive(:find_by_id).with(anything, '42').once.and_return(d1) ++ expect(Asana::Resources::Task).to receive(:find_by_id).with(anything, '42').once.and_return(d1) + + @asana.execute(data) + end +@@ -84,28 +85,28 @@ + ref https://app.asana.com/19292/956299/42 and closing https://app.asana.com/19292/956299/12 + EOF + data = create_data_for_commits(message) +- d1 = double('Asana::Task') ++ d1 = double('Asana::Resources::Task') + expect(d1).to receive(:add_comment) + expect(d1).to receive(:update).with(completed: true) +- expect(Asana::Task).to receive(:find_by_id).with(anything, '123').once.and_return(d1) ++ expect(Asana::Resources::Task).to receive(:find_by_id).with(anything, '123').once.and_return(d1) + +- d2 = double('Asana::Task') ++ d2 = double('Asana::Resources::Task') + expect(d2).to receive(:add_comment) + expect(d2).to receive(:update).with(completed: true) +- expect(Asana::Task).to receive(:find_by_id).with(anything, '456').once.and_return(d2) ++ expect(Asana::Resources::Task).to receive(:find_by_id).with(anything, '456').once.and_return(d2) + +- d3 = double('Asana::Task') ++ d3 = double('Asana::Resources::Task') + expect(d3).to receive(:add_comment) +- expect(Asana::Task).to receive(:find_by_id).with(anything, '789').once.and_return(d3) ++ expect(Asana::Resources::Task).to receive(:find_by_id).with(anything, '789').once.and_return(d3) + +- d4 = double('Asana::Task') ++ d4 = double('Asana::Resources::Task') + expect(d4).to receive(:add_comment) +- expect(Asana::Task).to receive(:find_by_id).with(anything, '42').once.and_return(d4) ++ expect(Asana::Resources::Task).to receive(:find_by_id).with(anything, '42').once.and_return(d4) + +- d5 = double('Asana::Task') ++ d5 = double('Asana::Resources::Task') + expect(d5).to receive(:add_comment) + expect(d5).to receive(:update).with(completed: true) +- expect(Asana::Task).to receive(:find_by_id).with(anything, '12').once.and_return(d5) ++ expect(Asana::Resources::Task).to receive(:find_by_id).with(anything, '12').once.and_return(d5) + + @asana.execute(data) + end +--- a/spec/models/user_spec.rb ++++ b/spec/models/user_spec.rb +@@ -2533,8 +2533,8 @@ + add_user(:maintainer) + end + +- it 'loads' do +- expect(user.ci_owned_runners).to contain_exactly(runner) ++ it 'does not load' do ++ expect(user.ci_owned_runners).to be_empty + end + end + +@@ -2549,6 +2549,20 @@ + end + end + ++ shared_examples :group_member do ++ context 'when the user is owner' do ++ before do ++ add_user(:owner) ++ end ++ ++ it 'loads' do ++ expect(user.ci_owned_runners).to contain_exactly(runner) ++ end ++ end ++ ++ it_behaves_like :member ++ end ++ + context 'with groups projects runners' do + let(:group) { create(:group) } + let!(:project) { create(:project, group: group) } +@@ -2557,7 +2571,7 @@ + group.add_user(user, access) + end + +- it_behaves_like :member ++ it_behaves_like :group_member + end + + context 'with groups runners' do +@@ -2568,14 +2582,14 @@ + group.add_user(user, access) + end + +- it_behaves_like :member ++ it_behaves_like :group_member + end + + context 'with other projects runners' do + let!(:project) { create(:project) } + + def add_user(access) +- project.add_role(user, access) ++ project.add_user(user, access) + end + + it_behaves_like :member +@@ -2593,7 +2607,7 @@ + subgroup.add_user(another_user, :owner) + end + +- it_behaves_like :member ++ it_behaves_like :group_member + end + end + +--- a/spec/requests/api/graphql/gitlab_schema_spec.rb ++++ b/spec/requests/api/graphql/gitlab_schema_spec.rb +@@ -8,6 +8,18 @@ + set(:project) { create(:project) } + + shared_examples 'imposing query limits' do ++ describe 'timeouts' do ++ context 'when timeout is reached' do ++ it 'shows an error' do ++ Timecop.scale(50000000) do # ludicrously large number because the timeout has to happen before the query even begins ++ subject ++ ++ expect_graphql_errors_to_include /Timeout/ ++ end ++ end ++ end ++ end ++ + describe '#max_complexity' do + context 'when complexity is too high' do + it 'shows an error' do +--- a/spec/requests/api/runners_spec.rb ++++ b/spec/requests/api/runners_spec.rb +@@ -6,6 +6,7 @@ + let(:admin) { create(:user, :admin) } + let(:user) { create(:user) } + let(:user2) { create(:user) } ++ let(:group_maintainer) { create(:user) } + + let(:project) { create(:project, creator_id: user.id) } + let(:project2) { create(:project, creator_id: user.id) } +@@ -20,6 +21,7 @@ + + before do + # Set project access for users ++ create(:group_member, :maintainer, user: group_maintainer, group: group) + create(:project_member, :maintainer, user: user, project: project) + create(:project_member, :maintainer, user: user, project: project2) + create(:project_member, :reporter, user: user2, project: project) +@@ -525,6 +527,20 @@ + end.to change { Ci::Runner.project_type.count }.by(-1) + end + ++ it 'does not delete group runner with maintainer access' do ++ delete api("/runners/#{group_runner.id}", group_maintainer) ++ ++ expect(response).to have_http_status(403) ++ end ++ ++ it 'deletes group runner with owner access' do ++ expect do ++ delete api("/runners/#{group_runner.id}", user) ++ ++ expect(response).to have_http_status(204) ++ end.to change { Ci::Runner.group_type.count }.by(-1) ++ end ++ + it_behaves_like '412 response' do + let(:request) { api("/runners/#{project_runner.id}", user) } + end diff --git a/debian/patches/series b/debian/patches/series index 98a757c873..a89b51c789 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -33,3 +33,4 @@ 0750-fix-relative-paths.patch 0760-bump-rubyzip.patch 0770-bump-node-d3.patch +gitlab-v12.5.4..v12.5.6.diff