Commit 02dd9b1b authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 3507ddd4 656a918a
...@@ -183,6 +183,7 @@ setup-test-env: ...@@ -183,6 +183,7 @@ setup-test-env:
- tmp/tests/gitlab-workhorse/gitlab-workhorse - tmp/tests/gitlab-workhorse/gitlab-workhorse
- tmp/tests/gitlab-workhorse/gitlab-resize-image - tmp/tests/gitlab-workhorse/gitlab-resize-image
- tmp/tests/gitlab-workhorse/config.toml - tmp/tests/gitlab-workhorse/config.toml
- tmp/tests/gitlab-workhorse/WORKHORSE_TREE
- tmp/tests/repositories/ - tmp/tests/repositories/
- tmp/tests/second_storage/ - tmp/tests/second_storage/
when: always when: always
......
...@@ -61,7 +61,9 @@ export default { ...@@ -61,7 +61,9 @@ export default {
<template> <template>
<section id="grafana" class="settings no-animate js-grafana-integration"> <section id="grafana" class="settings no-animate js-grafana-integration">
<div class="settings-header"> <div class="settings-header">
<h4 class="js-section-header"> <h4
class="js-section-header settings-title js-settings-toggle js-settings-toggle-trigger-only"
>
{{ s__('GrafanaIntegration|Grafana authentication') }} {{ s__('GrafanaIntegration|Grafana authentication') }}
</h4> </h4>
<gl-button class="js-settings-toggle">{{ __('Expand') }}</gl-button> <gl-button class="js-settings-toggle">{{ __('Expand') }}</gl-button>
......
...@@ -44,13 +44,14 @@ export default { ...@@ -44,13 +44,14 @@ export default {
</script> </script>
<template> <template>
<div class="dropdown"> <div class="dropdown">
<div class="js-pipeline-info"> <div class="js-pipeline-info" data-testid="pipeline-info">
<ci-icon :status="pipeline.details.status" class="vertical-align-middle" /> <ci-icon :status="pipeline.details.status" class="vertical-align-middle" />
<span class="font-weight-bold">{{ s__('Job|Pipeline') }}</span> <span class="font-weight-bold">{{ s__('Job|Pipeline') }}</span>
<gl-link <gl-link
:href="pipeline.path" :href="pipeline.path"
class="js-pipeline-path link-commit" class="js-pipeline-path link-commit"
data-testid="pipeline-path"
data-qa-selector="pipeline_path" data-qa-selector="pipeline_path"
>#{{ pipeline.id }}</gl-link >#{{ pipeline.id }}</gl-link
> >
...@@ -58,13 +59,17 @@ export default { ...@@ -58,13 +59,17 @@ export default {
{{ s__('Job|for') }} {{ s__('Job|for') }}
<template v-if="isTriggeredByMergeRequest"> <template v-if="isTriggeredByMergeRequest">
<gl-link :href="pipeline.merge_request.path" class="link-commit ref-name js-mr-link" <gl-link
:href="pipeline.merge_request.path"
class="link-commit ref-name"
data-testid="mr-link"
>!{{ pipeline.merge_request.iid }}</gl-link >!{{ pipeline.merge_request.iid }}</gl-link
> >
{{ s__('Job|with') }} {{ s__('Job|with') }}
<gl-link <gl-link
:href="pipeline.merge_request.source_branch_path" :href="pipeline.merge_request.source_branch_path"
class="link-commit ref-name js-source-branch-link" class="link-commit ref-name"
data-testid="source-branch-link"
>{{ pipeline.merge_request.source_branch }}</gl-link >{{ pipeline.merge_request.source_branch }}</gl-link
> >
...@@ -72,7 +77,8 @@ export default { ...@@ -72,7 +77,8 @@ export default {
{{ s__('Job|into') }} {{ s__('Job|into') }}
<gl-link <gl-link
:href="pipeline.merge_request.target_branch_path" :href="pipeline.merge_request.target_branch_path"
class="link-commit ref-name js-target-branch-link" class="link-commit ref-name"
data-testid="target-branch-link"
>{{ pipeline.merge_request.target_branch }}</gl-link >{{ pipeline.merge_request.target_branch }}</gl-link
> >
</template> </template>
......
...@@ -2,9 +2,9 @@ import initFilePickers from '~/file_pickers'; ...@@ -2,9 +2,9 @@ import initFilePickers from '~/file_pickers';
import BindInOut from '../../../../behaviors/bind_in_out'; import BindInOut from '../../../../behaviors/bind_in_out';
import Group from '../../../../group'; import Group from '../../../../group';
document.addEventListener('DOMContentLoaded', () => { (() => {
BindInOut.initAll(); BindInOut.initAll();
initFilePickers(); initFilePickers();
return new Group(); return new Group();
}); })();
import Labels from '../../../../labels'; import Labels from '../../../../labels';
document.addEventListener('DOMContentLoaded', () => new Labels()); new Labels(); // eslint-disable-line no-new
...@@ -7,46 +7,44 @@ import { __ } from '~/locale'; ...@@ -7,46 +7,44 @@ import { __ } from '~/locale';
import EmojiMenu from './emoji_menu'; import EmojiMenu from './emoji_menu';
const defaultStatusEmoji = 'speech_balloon'; const defaultStatusEmoji = 'speech_balloon';
const toggleEmojiMenuButtonSelector = '.js-toggle-emoji-menu';
const toggleEmojiMenuButton = document.querySelector(toggleEmojiMenuButtonSelector);
const statusEmojiField = document.getElementById('js-status-emoji-field');
const statusMessageField = document.getElementById('js-status-message-field');
document.addEventListener('DOMContentLoaded', () => { const toggleNoEmojiPlaceholder = (isVisible) => {
const toggleEmojiMenuButtonSelector = '.js-toggle-emoji-menu';
const toggleEmojiMenuButton = document.querySelector(toggleEmojiMenuButtonSelector);
const statusEmojiField = document.getElementById('js-status-emoji-field');
const statusMessageField = document.getElementById('js-status-message-field');
const toggleNoEmojiPlaceholder = (isVisible) => {
const placeholderElement = document.getElementById('js-no-emoji-placeholder'); const placeholderElement = document.getElementById('js-no-emoji-placeholder');
placeholderElement.classList.toggle('hidden', !isVisible); placeholderElement.classList.toggle('hidden', !isVisible);
}; };
const findStatusEmoji = () => toggleEmojiMenuButton.querySelector('gl-emoji'); const findStatusEmoji = () => toggleEmojiMenuButton.querySelector('gl-emoji');
const removeStatusEmoji = () => { const removeStatusEmoji = () => {
const statusEmoji = findStatusEmoji(); const statusEmoji = findStatusEmoji();
if (statusEmoji) { if (statusEmoji) {
statusEmoji.remove(); statusEmoji.remove();
} }
}; };
const selectEmojiCallback = (emoji, emojiTag) => { const selectEmojiCallback = (emoji, emojiTag) => {
statusEmojiField.value = emoji; statusEmojiField.value = emoji;
toggleNoEmojiPlaceholder(false); toggleNoEmojiPlaceholder(false);
removeStatusEmoji(); removeStatusEmoji();
toggleEmojiMenuButton.innerHTML += emojiTag; toggleEmojiMenuButton.innerHTML += emojiTag;
}; };
const clearEmojiButton = document.getElementById('js-clear-user-status-button'); const clearEmojiButton = document.getElementById('js-clear-user-status-button');
clearEmojiButton.addEventListener('click', () => { clearEmojiButton.addEventListener('click', () => {
statusEmojiField.value = ''; statusEmojiField.value = '';
statusMessageField.value = ''; statusMessageField.value = '';
removeStatusEmoji(); removeStatusEmoji();
toggleNoEmojiPlaceholder(true); toggleNoEmojiPlaceholder(true);
}); });
const emojiAutocomplete = new GfmAutoComplete(); const emojiAutocomplete = new GfmAutoComplete();
emojiAutocomplete.setup($(statusMessageField), { emojis: true }); emojiAutocomplete.setup($(statusMessageField), { emojis: true });
const userNameInput = document.getElementById('user_name'); const userNameInput = document.getElementById('user_name');
userNameInput.addEventListener('input', () => { userNameInput.addEventListener('input', () => {
const EMOJI_REGEX = emojiRegex(); const EMOJI_REGEX = emojiRegex();
if (EMOJI_REGEX.test(userNameInput.value)) { if (EMOJI_REGEX.test(userNameInput.value)) {
// set field to invalid so it gets detected by GlFieldErrors // set field to invalid so it gets detected by GlFieldErrors
...@@ -54,9 +52,9 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -54,9 +52,9 @@ document.addEventListener('DOMContentLoaded', () => {
} else { } else {
userNameInput.setCustomValidity(''); userNameInput.setCustomValidity('');
} }
}); });
Emoji.initEmojiMap() Emoji.initEmojiMap()
.then(() => { .then(() => {
const emojiMenu = new EmojiMenu( const emojiMenu = new EmojiMenu(
Emoji, Emoji,
...@@ -84,4 +82,3 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -84,4 +82,3 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
}) })
.catch(() => createFlash(__('Failed to load emoji list.'))); .catch(() => createFlash(__('Failed to load emoji list.')));
});
...@@ -11,7 +11,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -11,7 +11,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
# https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/30233 # https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/30233
before_action :set_application_setting, except: :integrations before_action :set_application_setting, except: :integrations
before_action :whitelist_query_limiting, only: [:usage_data] before_action :disable_query_limiting, only: [:usage_data]
before_action only: [:ci_cd] do before_action only: [:ci_cd] do
push_frontend_feature_flag(:ci_instance_variables_ui, default_enabled: true) push_frontend_feature_flag(:ci_instance_variables_ui, default_enabled: true)
...@@ -194,8 +194,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -194,8 +194,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
@plans = Plan.all @plans = Plan.all
end end
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/63107') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/63107')
end end
def application_setting_params def application_setting_params
......
...@@ -4,7 +4,7 @@ class Admin::ServicesController < Admin::ApplicationController ...@@ -4,7 +4,7 @@ class Admin::ServicesController < Admin::ApplicationController
include ServiceParams include ServiceParams
before_action :service, only: [:edit, :update] before_action :service, only: [:edit, :update]
before_action :whitelist_query_limiting, only: [:index] before_action :disable_query_limiting, only: [:index]
feature_category :integrations feature_category :integrations
...@@ -39,7 +39,7 @@ class Admin::ServicesController < Admin::ApplicationController ...@@ -39,7 +39,7 @@ class Admin::ServicesController < Admin::ApplicationController
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/220357') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/220357')
end end
end end
...@@ -13,7 +13,7 @@ module Boards ...@@ -13,7 +13,7 @@ module Boards
requires_cross_project_access if: -> { board&.group_board? } requires_cross_project_access if: -> { board&.group_board? }
before_action :whitelist_query_limiting, only: [:bulk_move] before_action :disable_query_limiting, only: [:bulk_move]
before_action :authorize_read_issue, only: [:index] before_action :authorize_read_issue, only: [:index]
before_action :authorize_create_issue, only: [:create] before_action :authorize_create_issue, only: [:create]
before_action :authorize_update_issue, only: [:update] before_action :authorize_update_issue, only: [:update]
...@@ -147,8 +147,8 @@ module Boards ...@@ -147,8 +147,8 @@ module Boards
serializer.represent(resource, opts) serializer.represent(resource, opts)
end end
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/35174') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/35174')
end end
def validate_id_list def validate_id_list
......
...@@ -4,7 +4,8 @@ class GraphqlController < ApplicationController ...@@ -4,7 +4,8 @@ class GraphqlController < ApplicationController
# Unauthenticated users have access to the API for public data # Unauthenticated users have access to the API for public data
skip_before_action :authenticate_user! skip_before_action :authenticate_user!
WHITELIST_HEADER = 'HTTP_X_GITLAB_QUERY_WHITELIST_ISSUE' # Header can be passed by tests to disable SQL query limits.
DISABLE_SQL_QUERY_LIMIT_HEADER = 'HTTP_X_GITLAB_DISABLE_SQL_QUERY_LIMIT'
# If a user is using their session to access GraphQL, we need to have session # If a user is using their session to access GraphQL, we need to have session
# storage, since the admin-mode check is session wide. # storage, since the admin-mode check is session wide.
...@@ -23,7 +24,7 @@ class GraphqlController < ApplicationController ...@@ -23,7 +24,7 @@ class GraphqlController < ApplicationController
before_action(only: [:execute]) { authenticate_sessionless_user!(:api) } before_action(only: [:execute]) { authenticate_sessionless_user!(:api) }
before_action :set_user_last_activity before_action :set_user_last_activity
before_action :track_vs_code_usage before_action :track_vs_code_usage
before_action :whitelist_query! before_action :disable_query_limiting
# Since we deactivate authentication from the main ApplicationController and # Since we deactivate authentication from the main ApplicationController and
# defer it to :authorize_access_api!, we need to override the bypass session # defer it to :authorize_access_api!, we need to override the bypass session
...@@ -62,12 +63,14 @@ class GraphqlController < ApplicationController ...@@ -62,12 +63,14 @@ class GraphqlController < ApplicationController
private private
# Tests may mark some queries as exempt from query limits # Tests may mark some GraphQL queries as exempt from SQL query limits
def whitelist_query! def disable_query_limiting
whitelist_issue = request.headers[WHITELIST_HEADER] return unless Gitlab::QueryLimiting.enabled_for_env?
return unless whitelist_issue
Gitlab::QueryLimiting.whitelist(whitelist_issue) disable_issue = request.headers[DISABLE_SQL_QUERY_LIMIT_HEADER]
return unless disable_issue
Gitlab::QueryLimiting.disable!(disable_issue)
end end
def set_user_last_activity def set_user_last_activity
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
class Import::GitlabProjectsController < Import::BaseController class Import::GitlabProjectsController < Import::BaseController
include WorkhorseAuthorization include WorkhorseAuthorization
before_action :whitelist_query_limiting, only: [:create] before_action :disable_query_limiting, only: [:create]
before_action :verify_gitlab_project_import_enabled before_action :verify_gitlab_project_import_enabled
def new def new
...@@ -42,8 +42,8 @@ class Import::GitlabProjectsController < Import::BaseController ...@@ -42,8 +42,8 @@ class Import::GitlabProjectsController < Import::BaseController
) )
end end
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437')
end end
def uploader_class def uploader_class
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
class Import::ManifestController < Import::BaseController class Import::ManifestController < Import::BaseController
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
before_action :whitelist_query_limiting, only: [:create] before_action :disable_query_limiting, only: [:create]
before_action :verify_import_enabled before_action :verify_import_enabled
before_action :ensure_import_vars, only: [:create, :status] before_action :ensure_import_vars, only: [:create, :status]
...@@ -115,7 +115,7 @@ class Import::ManifestController < Import::BaseController ...@@ -115,7 +115,7 @@ class Import::ManifestController < Import::BaseController
render_404 unless manifest_import_enabled? render_404 unless manifest_import_enabled?
end end
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/48939') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/48939')
end end
end end
...@@ -8,7 +8,7 @@ class Projects::CommitsController < Projects::ApplicationController ...@@ -8,7 +8,7 @@ class Projects::CommitsController < Projects::ApplicationController
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) } prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
around_action :allow_gitaly_ref_name_caching around_action :allow_gitaly_ref_name_caching
before_action :whitelist_query_limiting, except: :commits_root before_action :disable_query_limiting, except: :commits_root
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :assign_ref_vars, except: :commits_root before_action :assign_ref_vars, except: :commits_root
before_action :authorize_download_code! before_action :authorize_download_code!
...@@ -83,7 +83,7 @@ class Projects::CommitsController < Projects::ApplicationController ...@@ -83,7 +83,7 @@ class Projects::CommitsController < Projects::ApplicationController
@commits = set_commits_for_rendering(@commits) @commits = set_commits_for_rendering(@commits)
end end
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42330') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42330')
end end
end end
...@@ -7,7 +7,7 @@ class Projects::ForksController < Projects::ApplicationController ...@@ -7,7 +7,7 @@ class Projects::ForksController < Projects::ApplicationController
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
# Authorize # Authorize
before_action :whitelist_query_limiting, only: [:create] before_action :disable_query_limiting, only: [:create]
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :authorize_download_code! before_action :authorize_download_code!
before_action :authenticate_user!, only: [:new, :create] before_action :authenticate_user!, only: [:new, :create]
...@@ -110,8 +110,8 @@ class Projects::ForksController < Projects::ApplicationController ...@@ -110,8 +110,8 @@ class Projects::ForksController < Projects::ApplicationController
access_denied! unless fork_namespace && fork_service.valid_fork_target? access_denied! unless fork_namespace && fork_service.valid_fork_target?
end end
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42335') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42335')
end end
def load_namespaces_with_associations def load_namespaces_with_associations
......
...@@ -18,7 +18,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -18,7 +18,7 @@ class Projects::IssuesController < Projects::ApplicationController
prepend_before_action :authenticate_user!, only: [:new, :export_csv] prepend_before_action :authenticate_user!, only: [:new, :export_csv]
prepend_before_action :store_uri, only: [:new, :show, :designs] prepend_before_action :store_uri, only: [:new, :show, :designs]
before_action :whitelist_query_limiting, only: [:create, :create_merge_request, :move, :bulk_update] before_action :disable_query_limiting, only: [:create, :create_merge_request, :move, :bulk_update]
before_action :check_issues_available! before_action :check_issues_available!
before_action :issue, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) } before_action :issue, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) }
after_action :log_issue_show, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) } after_action :log_issue_show, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) }
...@@ -353,13 +353,13 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -353,13 +353,13 @@ class Projects::IssuesController < Projects::ApplicationController
IssuesFinder IssuesFinder
end end
def whitelist_query_limiting def disable_query_limiting
# Also see the following issues: # Also see the following issues:
# #
# 1. https://gitlab.com/gitlab-org/gitlab-foss/issues/42423 # 1. https://gitlab.com/gitlab-org/gitlab-foss/issues/42423
# 2. https://gitlab.com/gitlab-org/gitlab-foss/issues/42424 # 2. https://gitlab.com/gitlab-org/gitlab-foss/issues/42424
# 3. https://gitlab.com/gitlab-org/gitlab-foss/issues/42426 # 3. https://gitlab.com/gitlab-org/gitlab-foss/issues/42426
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42422') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42422')
end end
private private
......
...@@ -6,7 +6,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap ...@@ -6,7 +6,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
include RendersCommits include RendersCommits
skip_before_action :merge_request skip_before_action :merge_request
before_action :whitelist_query_limiting, only: [:create] before_action :disable_query_limiting, only: [:create]
before_action :authorize_create_merge_request_from! before_action :authorize_create_merge_request_from!
before_action :apply_diff_view_cookie!, only: [:diffs, :diff_for_path] before_action :apply_diff_view_cookie!, only: [:diffs, :diff_for_path]
before_action :build_merge_request, except: [:create] before_action :build_merge_request, except: [:create]
...@@ -133,8 +133,8 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap ...@@ -133,8 +133,8 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42384') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42384')
end end
def incr_count_webide_merge_request def incr_count_webide_merge_request
......
...@@ -14,7 +14,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -14,7 +14,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
skip_before_action :merge_request, only: [:index, :bulk_update, :export_csv] skip_before_action :merge_request, only: [:index, :bulk_update, :export_csv]
before_action :apply_diff_view_cookie!, only: [:show] before_action :apply_diff_view_cookie!, only: [:show]
before_action :whitelist_query_limiting, only: [:assign_related_issues, :update] before_action :disable_query_limiting, only: [:assign_related_issues, :update]
before_action :authorize_update_issuable!, only: [:close, :edit, :update, :remove_wip, :sort] before_action :authorize_update_issuable!, only: [:close, :edit, :update, :remove_wip, :sort]
before_action :authorize_read_actual_head_pipeline!, only: [ before_action :authorize_read_actual_head_pipeline!, only: [
:test_reports, :test_reports,
...@@ -468,9 +468,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -468,9 +468,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
access_denied! unless @merge_request.can_be_merged_by?(current_user) access_denied! unless @merge_request.can_be_merged_by?(current_user)
end end
def whitelist_query_limiting def disable_query_limiting
# Also see https://gitlab.com/gitlab-org/gitlab-foss/issues/42441 # Also see https://gitlab.com/gitlab-org/gitlab-foss/issues/42441
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42438') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42438')
end end
def reports_response(report_comparison, pipeline = nil) def reports_response(report_comparison, pipeline = nil)
......
...@@ -4,7 +4,7 @@ class Projects::NetworkController < Projects::ApplicationController ...@@ -4,7 +4,7 @@ class Projects::NetworkController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
include ApplicationHelper include ApplicationHelper
before_action :whitelist_query_limiting before_action :disable_query_limiting
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :assign_ref_vars before_action :assign_ref_vars
before_action :authorize_download_code! before_action :authorize_download_code!
...@@ -42,7 +42,7 @@ class Projects::NetworkController < Projects::ApplicationController ...@@ -42,7 +42,7 @@ class Projects::NetworkController < Projects::ApplicationController
@commit = @repo.commit(@options[:extended_sha1]) @commit = @repo.commit(@options[:extended_sha1])
end end
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42333') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42333')
end end
end end
...@@ -6,7 +6,7 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -6,7 +6,7 @@ class Projects::NotesController < Projects::ApplicationController
include NotesHelper include NotesHelper
include ToggleAwardEmoji include ToggleAwardEmoji
before_action :whitelist_query_limiting, only: [:create, :update] before_action :disable_query_limiting, only: [:create, :update]
before_action :authorize_read_note! before_action :authorize_read_note!
before_action :authorize_create_note!, only: [:create] before_action :authorize_create_note!, only: [:create]
before_action :authorize_resolve_note!, only: [:resolve, :unresolve] before_action :authorize_resolve_note!, only: [:resolve, :unresolve]
...@@ -87,7 +87,7 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -87,7 +87,7 @@ class Projects::NotesController < Projects::ApplicationController
access_denied! unless can?(current_user, :create_note, noteable) access_denied! unless can?(current_user, :create_note, noteable)
end end
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42383') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42383')
end end
end end
...@@ -4,7 +4,7 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -4,7 +4,7 @@ class Projects::PipelinesController < Projects::ApplicationController
include ::Gitlab::Utils::StrongMemoize include ::Gitlab::Utils::StrongMemoize
include Analytics::UniqueVisitsHelper include Analytics::UniqueVisitsHelper
before_action :whitelist_query_limiting, only: [:create, :retry] before_action :disable_query_limiting, only: [:create, :retry]
before_action :pipeline, except: [:index, :new, :create, :charts, :config_variables] before_action :pipeline, except: [:index, :new, :create, :charts, :config_variables]
before_action :set_pipeline_path, only: [:show] before_action :set_pipeline_path, only: [:show]
before_action :authorize_read_pipeline! before_action :authorize_read_pipeline!
...@@ -92,7 +92,7 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -92,7 +92,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end end
def show def show
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/26657') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/26657')
respond_to do |format| respond_to do |format|
format.html format.html
...@@ -269,9 +269,9 @@ class Projects::PipelinesController < Projects::ApplicationController ...@@ -269,9 +269,9 @@ class Projects::PipelinesController < Projects::ApplicationController
&.present(current_user: current_user) &.present(current_user: current_user)
end end
def whitelist_query_limiting def disable_query_limiting
# Also see https://gitlab.com/gitlab-org/gitlab-foss/issues/42343 # Also see https://gitlab.com/gitlab-org/gitlab-foss/issues/42343
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42339') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42339')
end end
def authorize_update_pipeline! def authorize_update_pipeline!
......
...@@ -14,7 +14,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -14,7 +14,7 @@ class ProjectsController < Projects::ApplicationController
around_action :allow_gitaly_ref_name_caching, only: [:index, :show] around_action :allow_gitaly_ref_name_caching, only: [:index, :show]
before_action :whitelist_query_limiting, only: [:show, :create] before_action :disable_query_limiting, only: [:show, :create]
before_action :authenticate_user!, except: [:index, :show, :activity, :refs, :resolve, :unfoldered_environment_names] before_action :authenticate_user!, except: [:index, :show, :activity, :refs, :resolve, :unfoldered_environment_names]
before_action :redirect_git_extension, only: [:show] before_action :redirect_git_extension, only: [:show]
before_action :project, except: [:index, :new, :create, :resolve] before_action :project, except: [:index, :new, :create, :resolve]
...@@ -510,8 +510,8 @@ class ProjectsController < Projects::ApplicationController ...@@ -510,8 +510,8 @@ class ProjectsController < Projects::ApplicationController
redirect_to(request.original_url.sub(%r{\.git/?\Z}, '')) redirect_to(request.original_url.sub(%r{\.git/?\Z}, ''))
end end
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/20826') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20826')
end end
def present_project def present_project
......
...@@ -9,7 +9,7 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -9,7 +9,7 @@ class RegistrationsController < Devise::RegistrationsController
layout 'devise' layout 'devise'
prepend_before_action :check_captcha, only: :create prepend_before_action :check_captcha, only: :create
before_action :whitelist_query_limiting, :ensure_destroy_prerequisites_met, only: [:destroy] before_action :disable_query_limiting, :ensure_destroy_prerequisites_met, only: [:destroy]
before_action :load_recaptcha, only: :new before_action :load_recaptcha, only: :new
before_action :set_invite_params, only: :new before_action :set_invite_params, only: :new
...@@ -162,8 +162,8 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -162,8 +162,8 @@ class RegistrationsController < Devise::RegistrationsController
@devise_mapping ||= Devise.mappings[:user] @devise_mapping ||= Devise.mappings[:user]
end end
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42380') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42380')
end end
def load_recaptcha def load_recaptcha
......
...@@ -18,7 +18,7 @@ module Mutations ...@@ -18,7 +18,7 @@ module Mutations
argument :api_url, GraphQL::STRING_TYPE, argument :api_url, GraphQL::STRING_TYPE,
required: true, required: true,
description: 'Endpoint at which prometheus can be queried.' description: 'Endpoint at which Prometheus can be queried.'
def resolve(args) def resolve(args)
project = authorized_find!(args[:project_path]) project = authorized_find!(args[:project_path])
......
...@@ -16,7 +16,7 @@ module Mutations ...@@ -16,7 +16,7 @@ module Mutations
argument :api_url, GraphQL::STRING_TYPE, argument :api_url, GraphQL::STRING_TYPE,
required: false, required: false,
description: "Endpoint at which prometheus can be queried." description: "Endpoint at which Prometheus can be queried."
def resolve(args) def resolve(args)
integration = authorized_find!(id: args[:id]) integration = authorized_find!(id: args[:id])
......
...@@ -53,7 +53,7 @@ module Mutations ...@@ -53,7 +53,7 @@ module Mutations
end end
def resolve(board:, **args) def resolve(board:, **args)
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/247861') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/247861')
raise_resource_not_available_error! unless board raise_resource_not_available_error! unless board
authorize_board!(board) authorize_board!(board)
......
...@@ -19,7 +19,7 @@ module Mutations ...@@ -19,7 +19,7 @@ module Mutations
def resolve(project_path:, iid:, assignee_usernames:, operation_mode: Types::MutationOperationModeEnum.enum[:replace]) def resolve(project_path:, iid:, assignee_usernames:, operation_mode: Types::MutationOperationModeEnum.enum[:replace])
resource = authorized_find!(project_path: project_path, iid: iid) resource = authorized_find!(project_path: project_path, iid: iid)
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/36098') if resource.is_a?(MergeRequest) Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/36098') if resource.is_a?(MergeRequest)
update_service_class.new( update_service_class.new(
resource.project, resource.project,
......
...@@ -11,7 +11,7 @@ module Mutations ...@@ -11,7 +11,7 @@ module Mutations
description: 'The project to move the issue to.' description: 'The project to move the issue to.'
def resolve(project_path:, iid:, target_project_path:) def resolve(project_path:, iid:, target_project_path:)
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/267762') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/267762')
issue = authorized_find!(project_path: project_path, iid: iid) issue = authorized_find!(project_path: project_path, iid: iid)
source_project = issue.project source_project = issue.project
......
...@@ -42,7 +42,8 @@ module Mutations ...@@ -42,7 +42,8 @@ module Mutations
description: 'Squash commits on the source branch before merge.' description: 'Squash commits on the source branch before merge.'
def resolve(project_path:, iid:, **args) def resolve(project_path:, iid:, **args)
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42317') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42317')
merge_request = authorized_find!(project_path: project_path, iid: iid) merge_request = authorized_find!(project_path: project_path, iid: iid)
project = merge_request.target_project project = merge_request.target_project
merge_params = args.compact.with_indifferent_access merge_params = args.compact.with_indifferent_access
......
...@@ -8,15 +8,16 @@ module Resolvers ...@@ -8,15 +8,16 @@ module Resolvers
argument :path, GraphQL::STRING_TYPE, argument :path, GraphQL::STRING_TYPE,
required: true, required: true,
description: "Path to a file which defines metrics dashboard " \ description: <<~MD
"eg: 'config/prometheus/common_metrics.yml'." Path to a file which defines a metrics dashboard eg: `"config/prometheus/common_metrics.yml"`.
MD
alias_method :environment, :object alias_method :environment, :object
def resolve(**args) def resolve(path:)
return unless environment return unless environment
::PerformanceMonitoring::PrometheusDashboard.find_for(**args, **service_params) ::PerformanceMonitoring::PrometheusDashboard.find_for(path: path, **service_params)
end end
private private
......
...@@ -7,7 +7,7 @@ module Resolvers ...@@ -7,7 +7,7 @@ module Resolvers
argument :ids, [GraphQL::ID_TYPE], argument :ids, [GraphQL::ID_TYPE],
required: false, required: false,
description: 'Array of global milestone IDs, e.g., "gid://gitlab/Milestone/1".' description: 'Array of global milestone IDs, e.g., `"gid://gitlab/Milestone/1"`.'
argument :state, Types::MilestoneStateEnum, argument :state, Types::MilestoneStateEnum,
required: false, required: false,
......
...@@ -7,6 +7,7 @@ module Ci ...@@ -7,6 +7,7 @@ module Ci
include StripAttribute include StripAttribute
include Schedulable include Schedulable
include Limitable include Limitable
include EachBatch
self.limit_name = 'ci_pipeline_schedules' self.limit_name = 'ci_pipeline_schedules'
self.limit_scope = :project self.limit_scope = :project
...@@ -28,6 +29,7 @@ module Ci ...@@ -28,6 +29,7 @@ module Ci
scope :active, -> { where(active: true) } scope :active, -> { where(active: true) }
scope :inactive, -> { where(active: false) } scope :inactive, -> { where(active: false) }
scope :preloaded, -> { preload(:owner, project: [:route]) } scope :preloaded, -> { preload(:owner, project: [:route]) }
scope :owned_by, ->(user) { where(owner: user) }
accepts_nested_attributes_for :variables, allow_destroy: true accepts_nested_attributes_for :variables, allow_destroy: true
......
...@@ -351,6 +351,7 @@ class User < ApplicationRecord ...@@ -351,6 +351,7 @@ class User < ApplicationRecord
# For this reason the tradeoff is to disable this cop. # For this reason the tradeoff is to disable this cop.
after_transition any => :blocked do |user| after_transition any => :blocked do |user|
Ci::CancelUserPipelinesService.new.execute(user) Ci::CancelUserPipelinesService.new.execute(user)
Ci::DisableUserPipelineSchedulesService.new.execute(user)
end end
# rubocop: enable CodeReuse/ServiceClass # rubocop: enable CodeReuse/ServiceClass
end end
......
# frozen_string_literal: true
module Ci
class DisableUserPipelineSchedulesService
def execute(user)
Ci::PipelineSchedule.active.owned_by(user).each_batch do |relation|
relation.update_all(active: false)
end
end
end
end
...@@ -48,7 +48,7 @@ class PostReceiveService ...@@ -48,7 +48,7 @@ class PostReceiveService
end end
def process_mr_push_options(push_options, changes) def process_mr_push_options(push_options, changes)
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/61359') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/61359')
return unless repository return unless repository
unless repository.repo_type.project? unless repository.repo_type.project?
......
---
title: Project Settings Operations header Grafana authentication expand/collapse on-click/on-tap
merge_request: 56270
author: Daniel Schömer
type: changed
---
title: Disable pipeline schedules when a user is blocked
merge_request: 56513
author:
type: changed
---
title: Add dast_profile_secret_variables table
merge_request: 56067
author:
type: added
# frozen_string_literal: true # frozen_string_literal: true
if Gitlab::QueryLimiting.enable? if Gitlab::QueryLimiting.enabled_for_env?
require_dependency 'gitlab/query_limiting/active_support_subscriber' require_dependency 'gitlab/query_limiting/active_support_subscriber'
require_dependency 'gitlab/query_limiting/transaction' require_dependency 'gitlab/query_limiting/transaction'
require_dependency 'gitlab/query_limiting/middleware' require_dependency 'gitlab/query_limiting/middleware'
......
# frozen_string_literal: true
class CreateDastSiteProfileVariables < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
table_comment = { owner: 'group::dynamic analysis', description: 'Secret variables used in DAST on-demand scans' }
encrypted_value_constraint_name = check_constraint_name(:dast_site_profile_secret_variables, 'encrypted_value', 'max_length')
encrypted_value_iv_constraint_name = check_constraint_name(:dast_site_profile_secret_variables, 'encrypted_value_iv', 'max_length')
create_table_with_constraints :dast_site_profile_secret_variables, comment: table_comment.to_json do |t|
t.references :dast_site_profile, null: false, foreign_key: { on_delete: :cascade }, index: false
t.timestamps_with_timezone
t.integer :variable_type, null: false, default: 1, limit: 2
t.text :key, null: false
t.binary :encrypted_value, null: false
t.binary :encrypted_value_iv, null: false, unique: true
t.index [:dast_site_profile_id, :key], unique: true, name: :index_site_profile_secret_variables_on_site_profile_id_and_key
t.text_limit :key, 255
# This does not currently have first-class support via create_table_with_constraints
t.check_constraint encrypted_value_constraint_name, 'length(encrypted_value) <= 13352'
t.check_constraint encrypted_value_iv_constraint_name, 'length(encrypted_value_iv) <= 17'
end
end
def down
drop_table :dast_site_profile_secret_variables
end
end
# frozen_string_literal: true
class AddOwnerAndIdIndexOnActiveCiPipelineSchedules < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_ci_pipeline_schedules_on_owner_id_and_id_and_active'
disable_ddl_transaction!
def up
add_concurrent_index :ci_pipeline_schedules, [:owner_id, :id], where: "active = TRUE", name: INDEX_NAME
end
def down
remove_concurrent_index_by_name :ci_pipeline_schedules, INDEX_NAME
end
end
d91eb442db670adef6d610a2c79259377709e5c98615ba10b85eb998715b3130
\ No newline at end of file
5d63a48f4a9327f683eff093d2862a0b88aa4249c94b2de9751ed6172c9b4799
\ No newline at end of file
...@@ -11808,6 +11808,31 @@ CREATE SEQUENCE dast_scanner_profiles_id_seq ...@@ -11808,6 +11808,31 @@ CREATE SEQUENCE dast_scanner_profiles_id_seq
ALTER SEQUENCE dast_scanner_profiles_id_seq OWNED BY dast_scanner_profiles.id; ALTER SEQUENCE dast_scanner_profiles_id_seq OWNED BY dast_scanner_profiles.id;
CREATE TABLE dast_site_profile_secret_variables (
id bigint NOT NULL,
dast_site_profile_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
variable_type smallint DEFAULT 1 NOT NULL,
key text NOT NULL,
encrypted_value bytea NOT NULL,
encrypted_value_iv bytea NOT NULL,
CONSTRAINT check_236213f179 CHECK ((length(encrypted_value) <= 13352)),
CONSTRAINT check_8cbef204b2 CHECK ((char_length(key) <= 255)),
CONSTRAINT check_b49080abbf CHECK ((length(encrypted_value_iv) <= 17))
);
COMMENT ON TABLE dast_site_profile_secret_variables IS '{"owner":"group::dynamic analysis","description":"Secret variables used in DAST on-demand scans"}';
CREATE SEQUENCE dast_site_profile_secret_variables_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE dast_site_profile_secret_variables_id_seq OWNED BY dast_site_profile_secret_variables.id;
CREATE TABLE dast_site_profiles ( CREATE TABLE dast_site_profiles (
id bigint NOT NULL, id bigint NOT NULL,
project_id bigint NOT NULL, project_id bigint NOT NULL,
...@@ -19141,6 +19166,8 @@ ALTER TABLE ONLY dast_profiles ALTER COLUMN id SET DEFAULT nextval('dast_profile ...@@ -19141,6 +19166,8 @@ ALTER TABLE ONLY dast_profiles ALTER COLUMN id SET DEFAULT nextval('dast_profile
ALTER TABLE ONLY dast_scanner_profiles ALTER COLUMN id SET DEFAULT nextval('dast_scanner_profiles_id_seq'::regclass); ALTER TABLE ONLY dast_scanner_profiles ALTER COLUMN id SET DEFAULT nextval('dast_scanner_profiles_id_seq'::regclass);
ALTER TABLE ONLY dast_site_profile_secret_variables ALTER COLUMN id SET DEFAULT nextval('dast_site_profile_secret_variables_id_seq'::regclass);
ALTER TABLE ONLY dast_site_profiles ALTER COLUMN id SET DEFAULT nextval('dast_site_profiles_id_seq'::regclass); ALTER TABLE ONLY dast_site_profiles ALTER COLUMN id SET DEFAULT nextval('dast_site_profiles_id_seq'::regclass);
ALTER TABLE ONLY dast_site_tokens ALTER COLUMN id SET DEFAULT nextval('dast_site_tokens_id_seq'::regclass); ALTER TABLE ONLY dast_site_tokens ALTER COLUMN id SET DEFAULT nextval('dast_site_tokens_id_seq'::regclass);
...@@ -20341,6 +20368,9 @@ ALTER TABLE ONLY dast_profiles ...@@ -20341,6 +20368,9 @@ ALTER TABLE ONLY dast_profiles
ALTER TABLE ONLY dast_scanner_profiles ALTER TABLE ONLY dast_scanner_profiles
ADD CONSTRAINT dast_scanner_profiles_pkey PRIMARY KEY (id); ADD CONSTRAINT dast_scanner_profiles_pkey PRIMARY KEY (id);
ALTER TABLE ONLY dast_site_profile_secret_variables
ADD CONSTRAINT dast_site_profile_secret_variables_pkey PRIMARY KEY (id);
ALTER TABLE ONLY dast_site_profiles ALTER TABLE ONLY dast_site_profiles
ADD CONSTRAINT dast_site_profiles_pkey PRIMARY KEY (id); ADD CONSTRAINT dast_site_profiles_pkey PRIMARY KEY (id);
...@@ -22015,6 +22045,8 @@ CREATE INDEX index_ci_pipeline_schedules_on_next_run_at_and_active ON ci_pipelin ...@@ -22015,6 +22045,8 @@ CREATE INDEX index_ci_pipeline_schedules_on_next_run_at_and_active ON ci_pipelin
CREATE INDEX index_ci_pipeline_schedules_on_owner_id ON ci_pipeline_schedules USING btree (owner_id); CREATE INDEX index_ci_pipeline_schedules_on_owner_id ON ci_pipeline_schedules USING btree (owner_id);
CREATE INDEX index_ci_pipeline_schedules_on_owner_id_and_id_and_active ON ci_pipeline_schedules USING btree (owner_id, id) WHERE (active = true);
CREATE INDEX index_ci_pipeline_schedules_on_project_id ON ci_pipeline_schedules USING btree (project_id); CREATE INDEX index_ci_pipeline_schedules_on_project_id ON ci_pipeline_schedules USING btree (project_id);
CREATE UNIQUE INDEX index_ci_pipeline_variables_on_pipeline_id_and_key ON ci_pipeline_variables USING btree (pipeline_id, key); CREATE UNIQUE INDEX index_ci_pipeline_variables_on_pipeline_id_and_key ON ci_pipeline_variables USING btree (pipeline_id, key);
...@@ -23679,6 +23711,8 @@ CREATE UNIQUE INDEX index_services_on_unique_group_id_and_type ON services USING ...@@ -23679,6 +23711,8 @@ CREATE UNIQUE INDEX index_services_on_unique_group_id_and_type ON services USING
CREATE UNIQUE INDEX index_shards_on_name ON shards USING btree (name); CREATE UNIQUE INDEX index_shards_on_name ON shards USING btree (name);
CREATE UNIQUE INDEX index_site_profile_secret_variables_on_site_profile_id_and_key ON dast_site_profile_secret_variables USING btree (dast_site_profile_id, key);
CREATE INDEX index_slack_integrations_on_service_id ON slack_integrations USING btree (service_id); CREATE INDEX index_slack_integrations_on_service_id ON slack_integrations USING btree (service_id);
CREATE UNIQUE INDEX index_slack_integrations_on_team_id_and_alias ON slack_integrations USING btree (team_id, alias); CREATE UNIQUE INDEX index_slack_integrations_on_team_id_and_alias ON slack_integrations USING btree (team_id, alias);
...@@ -25560,6 +25594,9 @@ ALTER TABLE ONLY operations_strategies_user_lists ...@@ -25560,6 +25594,9 @@ ALTER TABLE ONLY operations_strategies_user_lists
ALTER TABLE ONLY lfs_file_locks ALTER TABLE ONLY lfs_file_locks
ADD CONSTRAINT fk_rails_43df7a0412 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_43df7a0412 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY dast_site_profile_secret_variables
ADD CONSTRAINT fk_rails_43e2897950 FOREIGN KEY (dast_site_profile_id) REFERENCES dast_site_profiles(id) ON DELETE CASCADE;
ALTER TABLE ONLY merge_request_assignees ALTER TABLE ONLY merge_request_assignees
ADD CONSTRAINT fk_rails_443443ce6f FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_443443ce6f FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
...@@ -15,30 +15,30 @@ When a test fails because it executes more than 100 SQL queries there are two ...@@ -15,30 +15,30 @@ When a test fails because it executes more than 100 SQL queries there are two
solutions to this problem: solutions to this problem:
- Reduce the number of SQL queries that are executed. - Reduce the number of SQL queries that are executed.
- Whitelist the controller or API endpoint. - Disable query limiting for the controller or API endpoint.
You should only resort to whitelisting when an existing controller or endpoint You should only resort to disabling query limits when an existing controller or endpoint
is to blame as in this case reducing the number of SQL queries can take a lot of is to blame as in this case reducing the number of SQL queries can take a lot of
effort. Newly added controllers and endpoints are not allowed to execute more effort. Newly added controllers and endpoints are not allowed to execute more
than 100 SQL queries and no exceptions are made for this rule. _If_ a large than 100 SQL queries and no exceptions are made for this rule. _If_ a large
number of SQL queries is necessary to perform certain work it's best to have number of SQL queries is necessary to perform certain work it's best to have
this work performed by Sidekiq instead of doing this directly in a web request. this work performed by Sidekiq instead of doing this directly in a web request.
## Whitelisting ## Disable query limiting
In the event that you _have_ to whitelist a controller you must first In the event that you _have_ to disable query limits for a controller, you must first
create an issue. This issue should (preferably in the title) mention the create an issue. This issue should (preferably in the title) mention the
controller or endpoint and include the appropriate labels (`database`, controller or endpoint and include the appropriate labels (`database`,
`performance`, and at least a team specific label such as `Discussion`). `performance`, and at least a team specific label such as `Discussion`).
After the issue has been created you can whitelist the code in question. For After the issue has been created, you can disable query limits on the code in question. For
Rails controllers it's best to create a `before_action` hook that runs as early Rails controllers it's best to create a `before_action` hook that runs as early
as possible. The called method in turn should call as possible. The called method in turn should call
`Gitlab::QueryLimiting.whitelist('issue URL here')`. For example: `Gitlab::QueryLimiting.disable!('issue URL here')`. For example:
```ruby ```ruby
class MyController < ApplicationController class MyController < ApplicationController
before_action :whitelist_query_limiting, only: [:show] before_action :disable_query_limiting, only: [:show]
def index def index
# ... # ...
...@@ -48,8 +48,8 @@ class MyController < ApplicationController ...@@ -48,8 +48,8 @@ class MyController < ApplicationController
# ... # ...
end end
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/...') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/...')
end end
end end
``` ```
...@@ -63,7 +63,7 @@ call directly into the endpoint like so: ...@@ -63,7 +63,7 @@ call directly into the endpoint like so:
```ruby ```ruby
get '/projects/:id/foo' do get '/projects/:id/foo' do
Gitlab::QueryLimiting.whitelist('...') Gitlab::QueryLimiting.disable!('...')
# ... # ...
end end
......
---
stage: Plan
group: Product Planning
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Epic Boards **(ULTIMATE)**
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2864) in GitLab 13.10.
> - It's [deployed behind a feature flag](../../feature_flags.md), disabled by default.
> - It's disabled on GitLab.com.
> - It's not recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](../../../administration/feature_flags.md).
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
The GitLab Epic Board is a software project management tool used to plan,
organize, and visualize a workflow for a feature or product release.
Epic boards build on the existing [epic tracking functionality](index.md) and
[labels](../../project/labels.md). Your epics appear as cards in vertical lists, organized by their assigned
labels.
To view an epic board, in a group, select **Epics > Boards**.
![GitLab epic board - Ultimate](img/epic_board_v13_10.png)
## Create an epic board
To create a new epic board:
1. Select the dropdown with the current board name in the upper left corner of the Epic Boards page.
1. Select **Create new board**.
1. Enter the new board's name and select **Create**.
## Limitations of epic boards
As of GitLab 13.10, these limitations apply:
- Epic Boards need to be enabled by an administrator.
- Epic Boards can be created but not deleted.
- Lists can be added to the board but not deleted.
- There is no sidebar on the board. To edit an epic, go to the epic's page.
- There is no drag and drop support yet. To move an epic between lists, edit epic labels on the epic's page.
- Epics cannot be re-ordered within the list.
To learn more about the future iterations of this feature, visit
[epic 5067](https://gitlab.com/groups/gitlab-org/-/epics/5067).
## Enable or disable Epic Boards
Epic Boards are under development and not ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can enable it.
To enable it:
```ruby
Feature.enable(:epic_boards)
```
To disable it:
```ruby
Feature.disable(:epic_boards)
```
...@@ -9,8 +9,7 @@ module EE ...@@ -9,8 +9,7 @@ module EE
prepended do prepended do
include DescriptionDiffActions include DescriptionDiffActions
before_action :whitelist_query_limiting_ee, only: [:update] before_action :disable_query_limiting_ee, only: [:update]
before_action only: [:new, :create] do before_action only: [:new, :create] do
populate_vulnerability_id populate_vulnerability_id
end end
...@@ -43,8 +42,8 @@ module EE ...@@ -43,8 +42,8 @@ module EE
options.reject { |key| key == 'weight' } options.reject { |key| key == 'weight' }
end end
def whitelist_query_limiting_ee def disable_query_limiting_ee
::Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/4794') ::Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/4794')
end end
def issue_params def issue_params
......
...@@ -16,7 +16,7 @@ module EE ...@@ -16,7 +16,7 @@ module EE
push_frontend_feature_flag(:usage_data_i_testing_load_performance_widget_total, @project, default_enabled: true) push_frontend_feature_flag(:usage_data_i_testing_load_performance_widget_total, @project, default_enabled: true)
end end
before_action :whitelist_query_limiting_ee_merge, only: [:merge] before_action :disable_query_limiting_ee_merge, only: [:merge]
before_action :authorize_read_pipeline!, only: [:container_scanning_reports, :dependency_scanning_reports, before_action :authorize_read_pipeline!, only: [:container_scanning_reports, :dependency_scanning_reports,
:sast_reports, :secret_detection_reports, :dast_reports, :sast_reports, :secret_detection_reports, :dast_reports,
:metrics_reports, :coverage_fuzzing_reports, :metrics_reports, :coverage_fuzzing_reports,
...@@ -64,8 +64,8 @@ module EE ...@@ -64,8 +64,8 @@ module EE
private private
def whitelist_query_limiting_ee_merge def disable_query_limiting_ee_merge
::Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/4792') ::Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/4792')
end end
end end
end end
......
...@@ -8,6 +8,8 @@ module Dast ...@@ -8,6 +8,8 @@ module Dast
belongs_to :dast_site_profile belongs_to :dast_site_profile
belongs_to :dast_scanner_profile belongs_to :dast_scanner_profile
has_many :secret_variables, through: :dast_site_profile, class_name: 'Dast::SiteProfileSecretVariable'
validates :description, length: { maximum: 255 } validates :description, length: { maximum: 255 }
validates :name, length: { maximum: 255 }, uniqueness: { scope: :project_id }, presence: true validates :name, length: { maximum: 255 }, uniqueness: { scope: :project_id }, presence: true
validates :branch_name, length: { maximum: 255 } validates :branch_name, length: { maximum: 255 }
......
# frozen_string_literal: true
module Dast
class SiteProfileSecretVariable < ApplicationRecord
include Ci::HasVariable
include Ci::Maskable
self.table_name = 'dast_site_profile_secret_variables'
belongs_to :dast_site_profile
delegate :project, to: :dast_site_profile, allow_nil: false
attribute :masked, default: true
attr_encrypted :value,
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
key: Settings.attr_encrypted_db_key_base_32,
encode: false # No need to encode for binary column https://github.com/attr-encrypted/attr_encrypted#the-encode-encode_iv-encode_salt-and-default_encoding-options
# Secret variables must be masked to prevent them being readable in CI jobs
validates :masked, inclusion: { in: [true] }
validates :variable_type, inclusion: { in: ['env_var'] }
validates :key, uniqueness: { scope: :dast_site_profile_id, message: "(%{value}) has already been taken" }
# Since user input is base64 encoded before being encrypted, we must validate against the encoded length
MAX_VALUE_LENGTH = 10_000
MAX_ENCODED_VALUE_LENGTH = ((4 * MAX_VALUE_LENGTH / 3) + 3) & ~3
validates :value, length: {
maximum: MAX_ENCODED_VALUE_LENGTH, # encoded user input length
too_long: -> (object, data) { "exceeds the #{MAX_VALUE_LENGTH} character limit" } # user input length
}
# User input is base64 encoded before being encrypted in order to allow it to be masked by default
def raw_value=(new_value)
self.value = Base64.strict_encode64(new_value)
end
# Use #raw_value= to ensure value is maskable
private :value=
end
end
...@@ -4,6 +4,8 @@ class DastSiteProfile < ApplicationRecord ...@@ -4,6 +4,8 @@ class DastSiteProfile < ApplicationRecord
belongs_to :project belongs_to :project
belongs_to :dast_site belongs_to :dast_site
has_many :secret_variables, class_name: 'Dast::SiteProfileSecretVariable'
validates :name, length: { maximum: 255 }, uniqueness: { scope: :project_id }, presence: true validates :name, length: { maximum: 255 }, uniqueness: { scope: :project_id }, presence: true
validates :project_id, :dast_site_id, presence: true validates :project_id, :dast_site_id, presence: true
validate :dast_site_project_id_fk validate :dast_site_project_id_fk
......
...@@ -115,7 +115,7 @@ module API ...@@ -115,7 +115,7 @@ module API
at_least_one_of :title, :description, :start_date_fixed, :start_date_is_fixed, :due_date_fixed, :due_date_is_fixed, :labels, :add_labels, :remove_labels, :state_event, :confidential at_least_one_of :title, :description, :start_date_fixed, :start_date_is_fixed, :due_date_fixed, :due_date_is_fixed, :labels, :add_labels, :remove_labels, :state_event, :confidential
end end
put ':id/(-/)epics/:epic_iid' do put ':id/(-/)epics/:epic_iid' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/194104') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/194104')
authorize_can_admin_epic! authorize_can_admin_epic!
......
...@@ -90,7 +90,7 @@ module EE ...@@ -90,7 +90,7 @@ module EE
desc: 'Array of Group IDs to set as approvers.' desc: 'Array of Group IDs to set as approvers.'
end end
put 'approvers' do put 'approvers' do
::Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/8883') ::Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/8883')
merge_request = find_merge_request_with_access(params[:merge_request_iid], :update_approvers) merge_request = find_merge_request_with_access(params[:merge_request_iid], :update_approvers)
......
# frozen_string_literal: true
FactoryBot.define do
factory :dast_site_profile_secret_variable, class: 'Dast::SiteProfileSecretVariable' do
dast_site_profile
sequence(:key) { |n| "VARIABLE_#{n}" }
raw_value { 'VARIABLE_VALUE' }
end
end
...@@ -9,6 +9,7 @@ RSpec.describe Dast::Profile, type: :model do ...@@ -9,6 +9,7 @@ RSpec.describe Dast::Profile, type: :model do
it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:dast_site_profile) } it { is_expected.to belong_to(:dast_site_profile) }
it { is_expected.to belong_to(:dast_scanner_profile) } it { is_expected.to belong_to(:dast_scanner_profile) }
it { is_expected.to have_many(:secret_variables).through(:dast_site_profile).class_name('Dast::SiteProfileSecretVariable') }
end end
describe 'validations' do describe 'validations' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Dast::SiteProfileSecretVariable, type: :model do
let_it_be(:dast_site_profile) { create(:dast_site_profile) }
subject { create(:dast_site_profile_secret_variable, dast_site_profile: dast_site_profile) }
it_behaves_like 'CI variable'
describe 'constants' do
describe 'MAX_ENCODED_VALUE_LENGTH' do
it 'correctly expresses the relationship between input and encoded length' do
raw_value = SecureRandom.alphanumeric(described_class::MAX_VALUE_LENGTH)
expect(described_class::MAX_ENCODED_VALUE_LENGTH).to eq(Base64.strict_encode64(raw_value).length)
end
end
end
describe 'associations' do
it { is_expected.to belong_to(:dast_site_profile) }
end
describe 'validations' do
it { is_expected.to be_valid }
it { is_expected.to include_module(Ci::Maskable) }
it { is_expected.to include_module(Ci::HasVariable) }
it { is_expected.to validate_inclusion_of(:masked).in_array([true]) }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:dast_site_profile_id).with_message(/\(\w+\) has already been taken/) }
it 'only allows records where variable_type=env_var', :aggregate_failures do
subject = build(:dast_site_profile_secret_variable, variable_type: :file)
expect(subject).not_to be_valid
expect(subject.errors.full_messages).to include('Variable type is not included in the list')
end
describe '#value' do
subject { build(:dast_site_profile_secret_variable, dast_site_profile: dast_site_profile, raw_value: raw_value) }
context 'when the value is over the limit' do
let(:raw_value) { SecureRandom.alphanumeric(10_003) }
it 'is not valid', :aggregate_failures do
expect(subject).not_to be_valid
expect(subject.errors.full_messages).to include('Value exceeds the 10000 character limit')
end
it 'raises a database level error' do
allow(subject).to receive(:valid?).and_return(true)
expect { subject.save! }.to raise_error(ActiveRecord::StatementInvalid)
end
end
context 'when value is under the limit' do
let(:raw_value) { SecureRandom.alphanumeric(10_000) }
it 'is valid' do
expect(subject).to be_valid
end
it 'does not raise database level error' do
allow(subject).to receive(:valid?).and_return(true)
expect { subject.save! }.not_to raise_error
end
end
end
end
describe '#masked' do
it 'defaults to true', :aggregate_failures do
expect(subject.masked).to eq(true)
expect(described_class.new.masked).to eq(true)
end
end
describe '#project' do
it 'delegates to dast_site_profile' do
expect(subject.project).to eq(subject.dast_site_profile.project)
end
end
describe '#raw_value=' do
it 'pre-encodes the value' do
value = SecureRandom.alphanumeric
subject = create(:dast_site_profile_secret_variable, raw_value: value)
expect(Base64.strict_decode64(subject.value)).to eq(value)
end
end
describe '#value=' do
it 'raises an error because #raw_value= should be used instead' do
expect { subject.value = SecureRandom.alphanumeric }.to raise_error(NoMethodError, /private method `value=' called for/)
end
end
describe '#variable_type' do
it 'defaults to env_var', :aggregate_failures do
variable_type = 'env_var'
expect(subject.variable_type).to eq(variable_type)
expect(described_class.new.variable_type).to eq(variable_type)
end
end
end
...@@ -8,6 +8,7 @@ RSpec.describe DastSiteProfile, type: :model do ...@@ -8,6 +8,7 @@ RSpec.describe DastSiteProfile, type: :model do
describe 'associations' do describe 'associations' do
it { is_expected.to belong_to(:project) } it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:dast_site) } it { is_expected.to belong_to(:dast_site) }
it { is_expected.to have_many(:secret_variables).class_name('Dast::SiteProfileSecretVariable') }
end end
describe 'validations' do describe 'validations' do
......
...@@ -70,7 +70,7 @@ module API ...@@ -70,7 +70,7 @@ module API
optional :variables, Array, desc: 'Array of variables available in the pipeline' optional :variables, Array, desc: 'Array of variables available in the pipeline'
end end
post ':id/pipeline' do post ':id/pipeline' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42124') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42124')
authorize! :create_pipeline, user_project authorize! :create_pipeline, user_project
......
...@@ -112,7 +112,7 @@ module API ...@@ -112,7 +112,7 @@ module API
end end
def delete_group(group) def delete_group(group)
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/46285') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/46285')
destroy_conditionally!(group) do |group| destroy_conditionally!(group) do |group|
::Groups::DestroyService.new(group, current_user).async_execute ::Groups::DestroyService.new(group, current_user).async_execute
end end
......
...@@ -116,7 +116,7 @@ module API ...@@ -116,7 +116,7 @@ module API
end end
def create_note(noteable, opts) def create_note(noteable, opts)
whitelist_query_limiting disable_query_limiting
authorize!(:create_note, noteable) authorize!(:create_note, noteable)
parent = noteable_parent(noteable) parent = noteable_parent(noteable)
...@@ -144,8 +144,8 @@ module API ...@@ -144,8 +144,8 @@ module API
present discussion, with: Entities::Discussion present discussion, with: Entities::Discussion
end end
def whitelist_query_limiting def disable_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/-/issues/211538') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/211538')
end end
end end
end end
......
...@@ -242,7 +242,7 @@ module API ...@@ -242,7 +242,7 @@ module API
use :issue_params use :issue_params
end end
post ':id/issues' do post ':id/issues' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42320') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42320')
check_rate_limit! :issues_create, [current_user] check_rate_limit! :issues_create, [current_user]
...@@ -288,7 +288,7 @@ module API ...@@ -288,7 +288,7 @@ module API
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
put ':id/issues/:issue_iid' do put ':id/issues/:issue_iid' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42322') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42322')
issue = user_project.issues.find_by!(iid: params.delete(:issue_iid)) issue = user_project.issues.find_by!(iid: params.delete(:issue_iid))
authorize! :update_issue, issue authorize! :update_issue, issue
...@@ -346,7 +346,7 @@ module API ...@@ -346,7 +346,7 @@ module API
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
post ':id/issues/:issue_iid/move' do post ':id/issues/:issue_iid/move' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42323') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42323')
issue = user_project.issues.find_by(iid: params[:issue_iid]) issue = user_project.issues.find_by(iid: params[:issue_iid])
not_found!('Issue') unless issue not_found!('Issue') unless issue
......
...@@ -207,7 +207,7 @@ module API ...@@ -207,7 +207,7 @@ module API
use :optional_params use :optional_params
end end
post ":id/merge_requests" do post ":id/merge_requests" do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42316') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42316')
authorize! :create_merge_request_from, user_project authorize! :create_merge_request_from, user_project
...@@ -416,7 +416,7 @@ module API ...@@ -416,7 +416,7 @@ module API
at_least_one_of(*::API::MergeRequests.update_params_at_least_one_of) at_least_one_of(*::API::MergeRequests.update_params_at_least_one_of)
end end
put ':id/merge_requests/:merge_request_iid' do put ':id/merge_requests/:merge_request_iid' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42318') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42318')
merge_request = find_merge_request_with_access(params.delete(:merge_request_iid), :update_merge_request) merge_request = find_merge_request_with_access(params.delete(:merge_request_iid), :update_merge_request)
...@@ -445,7 +445,7 @@ module API ...@@ -445,7 +445,7 @@ module API
optional :squash, type: Grape::API::Boolean, desc: 'When true, the commits will be squashed into a single commit on merge' optional :squash, type: Grape::API::Boolean, desc: 'When true, the commits will be squashed into a single commit on merge'
end end
put ':id/merge_requests/:merge_request_iid/merge' do put ':id/merge_requests/:merge_request_iid/merge' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42317') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42317')
merge_request = find_project_merge_request(params[:merge_request_iid]) merge_request = find_project_merge_request(params[:merge_request_iid])
......
...@@ -67,7 +67,7 @@ module API ...@@ -67,7 +67,7 @@ module API
check_rate_limit! :project_import, [current_user, :project_import] check_rate_limit! :project_import, [current_user, :project_import]
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437')
validate_file! validate_file!
......
...@@ -215,7 +215,7 @@ module API ...@@ -215,7 +215,7 @@ module API
use :create_params use :create_params
end end
post do post do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/21139') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/21139')
attrs = declared_params(include_missing: false) attrs = declared_params(include_missing: false)
attrs = translate_params_for_compatibility(attrs) attrs = translate_params_for_compatibility(attrs)
filter_attributes_using_license!(attrs) filter_attributes_using_license!(attrs)
...@@ -248,7 +248,7 @@ module API ...@@ -248,7 +248,7 @@ module API
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
post "user/:user_id", feature_category: :projects do post "user/:user_id", feature_category: :projects do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/21139') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/21139')
authenticated_as_admin! authenticated_as_admin!
user = User.find_by(id: params.delete(:user_id)) user = User.find_by(id: params.delete(:user_id))
not_found!('User') unless user not_found!('User') unless user
...@@ -310,7 +310,7 @@ module API ...@@ -310,7 +310,7 @@ module API
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the fork' optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the fork'
end end
post ':id/fork', feature_category: :source_code_management do post ':id/fork', feature_category: :source_code_management do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42284') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42284')
not_found! unless can?(current_user, :fork_project, user_project) not_found! unless can?(current_user, :fork_project, user_project)
......
...@@ -21,7 +21,7 @@ module API ...@@ -21,7 +21,7 @@ module API
optional :variables, type: Hash, desc: 'The list of variables to be injected into build' optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
end end
post ":id/(ref/:ref/)trigger/pipeline", requirements: { ref: /.+/ } do post ":id/(ref/:ref/)trigger/pipeline", requirements: { ref: /.+/ } do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42283') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42283')
forbidden! if gitlab_pipeline_hook_request? forbidden! if gitlab_pipeline_hook_request?
......
...@@ -571,7 +571,7 @@ module API ...@@ -571,7 +571,7 @@ module API
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
delete ":id", feature_category: :users do delete ":id", feature_category: :users do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab/issues/20757') Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/20757')
authenticated_as_admin! authenticated_as_admin!
......
...@@ -6,28 +6,36 @@ module Gitlab ...@@ -6,28 +6,36 @@ module Gitlab
# #
# This is only enabled in development and test to ensure we don't produce # This is only enabled in development and test to ensure we don't produce
# any errors that users of other environments can't do anything about themselves. # any errors that users of other environments can't do anything about themselves.
def self.enable? def self.enabled_for_env?
Rails.env.development? || Rails.env.test? Rails.env.development? || Rails.env.test?
end end
def self.enabled?
enabled_for_env? &&
!Gitlab::SafeRequestStore[:query_limiting_disabled]
end
# Allows the current request to execute any number of SQL queries. # Allows the current request to execute any number of SQL queries.
# #
# This method should _only_ be used when there's a corresponding issue to # This method should _only_ be used when there's a corresponding issue to
# reduce the number of queries. # reduce the number of queries.
# #
# The issue URL is only meant to push developers into creating an issue # The issue URL is only meant to push developers into creating an issue
# instead of blindly whitelisting offending blocks of code. # instead of blindly disabling for offending blocks of code.
def self.whitelist(issue_url) def self.disable!(issue_url)
return unless enable?
unless issue_url.start_with?('https://') unless issue_url.start_with?('https://')
raise( raise(
ArgumentError, ArgumentError,
'You must provide a valid issue URL in order to whitelist a block of code' 'You must provide a valid issue URL in order to allow a block of code'
) )
end end
Transaction&.current&.whitelisted = true Gitlab::SafeRequestStore[:query_limiting_disabled] = true
end
# Enables query limiting for the request.
def self.enable!
Gitlab::SafeRequestStore[:query_limiting_disabled] = nil
end end
end end
end end
...@@ -5,7 +5,7 @@ module Gitlab ...@@ -5,7 +5,7 @@ module Gitlab
class Transaction class Transaction
THREAD_KEY = :__gitlab_query_counts_transaction THREAD_KEY = :__gitlab_query_counts_transaction
attr_accessor :count, :whitelisted attr_accessor :count
# The name of the action (e.g. `UsersController#show`) that is being # The name of the action (e.g. `UsersController#show`) that is being
# executed. # executed.
...@@ -45,7 +45,6 @@ module Gitlab ...@@ -45,7 +45,6 @@ module Gitlab
def initialize def initialize
@action = nil @action = nil
@count = 0 @count = 0
@whitelisted = false
@sql_executed = [] @sql_executed = []
end end
...@@ -59,7 +58,7 @@ module Gitlab ...@@ -59,7 +58,7 @@ module Gitlab
end end
def increment def increment
@count += 1 unless whitelisted @count += 1 if enabled?
end end
def executed_sql(sql) def executed_sql(sql)
...@@ -83,6 +82,10 @@ module Gitlab ...@@ -83,6 +82,10 @@ module Gitlab
["#{header}: #{msg}", log, ellipsis].compact.join("\n") ["#{header}: #{msg}", log, ellipsis].compact.join("\n")
end end
def enabled?
::Gitlab::QueryLimiting.enabled?
end
end end
end end
end end
...@@ -709,9 +709,9 @@ RSpec.describe 'Pipeline', :js do ...@@ -709,9 +709,9 @@ RSpec.describe 'Pipeline', :js do
end end
end end
it 'displays the PipelineSchedule in an active state' do it 'displays the PipelineSchedule in an inactive state' do
visit project_pipeline_schedules_path(project) visit project_pipeline_schedules_path(project)
page.click_link('Active') page.click_link('Inactive')
expect(page).to have_selector('table.ci-table > tbody > tr > td', text: 'blocked user schedule') expect(page).to have_selector('table.ci-table > tbody > tr > td', text: 'blocked user schedule')
end end
......
...@@ -9,7 +9,7 @@ exports[`grafana integration component default state to match the default snapsh ...@@ -9,7 +9,7 @@ exports[`grafana integration component default state to match the default snapsh
class="settings-header" class="settings-header"
> >
<h4 <h4
class="js-section-header" class="js-section-header settings-title js-settings-toggle js-settings-toggle-trigger-only"
> >
Grafana authentication Grafana authentication
......
import Vue from 'vue'; import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { trimText } from 'helpers/text_helper'; import { trimText } from 'helpers/text_helper';
import mountComponent from 'helpers/vue_mount_component_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import component from '~/jobs/components/stages_dropdown.vue'; import StagesDropdown from '~/jobs/components/stages_dropdown.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import {
mockPipelineWithoutMR,
mockPipelineWithAttachedMR,
mockPipelineDetached,
} from '../mock_data';
describe('Stages Dropdown', () => { describe('Stages Dropdown', () => {
const Component = Vue.extend(component); let wrapper;
let vm;
const findStatus = () => wrapper.findComponent(CiIcon);
const mockPipelineData = { const findSelectedStageText = () => wrapper.findComponent(GlDropdown).props('text');
id: 28029444, const findStageItem = (index) => wrapper.findAllComponents(GlDropdownItem).at(index);
details: {
status: { const findPipelineInfoText = () => wrapper.findByTestId('pipeline-info').text();
details_path: '/gitlab-org/gitlab-foss/pipelines/28029444', const findPipelinePath = () => wrapper.findByTestId('pipeline-path').attributes('href');
group: 'success', const findMRLinkPath = () => wrapper.findByTestId('mr-link').attributes('href');
has_details: true, const findSourceBranchLinkPath = () =>
icon: 'status_success', wrapper.findByTestId('source-branch-link').attributes('href');
label: 'passed', const findTargetBranchLinkPath = () =>
text: 'passed', wrapper.findByTestId('target-branch-link').attributes('href');
tooltip: 'passed',
}, const createComponent = (props) => {
}, wrapper = extendedWrapper(
path: 'pipeline/28029444', shallowMount(StagesDropdown, {
flags: { propsData: {
merge_request_pipeline: true, ...props,
detached_merge_request_pipeline: false,
},
merge_request: {
iid: 1234,
path: '/root/detached-merge-request-pipelines/-/merge_requests/1',
title: 'Update README.md',
source_branch: 'feature-1234',
source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1234',
target_branch: 'master',
target_branch_path: '/root/detached-merge-request-pipelines/branches/master',
},
ref: {
name: 'test-branch',
}, },
}),
);
}; };
describe('without a merge request pipeline', () => { afterEach(() => {
let pipeline; wrapper.destroy();
});
describe('without a merge request pipeline', () => {
beforeEach(() => { beforeEach(() => {
pipeline = JSON.parse(JSON.stringify(mockPipelineData)); createComponent({
delete pipeline.merge_request; pipeline: mockPipelineWithoutMR,
delete pipeline.flags.merge_request_pipeline;
delete pipeline.flags.detached_merge_request_pipeline;
vm = mountComponent(Component, {
pipeline,
stages: [{ name: 'build' }, { name: 'test' }], stages: [{ name: 'build' }, { name: 'test' }],
selectedStage: 'deploy', selectedStage: 'deploy',
}); });
}); });
afterEach(() => {
vm.$destroy();
});
it('renders pipeline status', () => { it('renders pipeline status', () => {
expect(vm.$el.querySelector('.js-ci-status-icon-success')).not.toBeNull(); expect(findStatus().exists()).toBe(true);
}); });
it('renders pipeline link', () => { it('renders pipeline link', () => {
expect(vm.$el.querySelector('.js-pipeline-path').getAttribute('href')).toEqual( expect(findPipelinePath()).toBe('pipeline/28029444');
'pipeline/28029444',
);
}); });
it('renders dropdown with stages', () => { it('renders dropdown with stages', () => {
expect(vm.$el.querySelector('.dropdown .js-stage-item').textContent).toContain('build'); expect(findStageItem(0).text()).toBe('build');
}); });
it('rendes selected stage', () => { it('rendes selected stage', () => {
expect(vm.$el.querySelector('.dropdown .js-selected-stage').textContent).toContain('deploy'); expect(findSelectedStageText()).toBe('deploy');
}); });
it(`renders the pipeline info text like "Pipeline #123 for source_branch"`, () => { it(`renders the pipeline info text like "Pipeline #123 for source_branch"`, () => {
const expected = `Pipeline #${pipeline.id} for ${pipeline.ref.name}`; const expected = `Pipeline #${mockPipelineWithoutMR.id} for ${mockPipelineWithoutMR.ref.name}`;
const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText); const actual = trimText(findPipelineInfoText());
expect(actual).toBe(expected); expect(actual).toBe(expected);
}); });
}); });
describe('with an "attached" merge request pipeline', () => { describe('with an "attached" merge request pipeline', () => {
let pipeline;
beforeEach(() => { beforeEach(() => {
pipeline = JSON.parse(JSON.stringify(mockPipelineData)); createComponent({
pipeline.flags.merge_request_pipeline = true; pipeline: mockPipelineWithAttachedMR,
pipeline.flags.detached_merge_request_pipeline = false;
vm = mountComponent(Component, {
pipeline,
stages: [], stages: [],
selectedStage: 'deploy', selectedStage: 'deploy',
}); });
}); });
it(`renders the pipeline info text like "Pipeline #123 for !456 with source_branch into target_branch"`, () => { it(`renders the pipeline info text like "Pipeline #123 for !456 with source_branch into target_branch"`, () => {
const expected = `Pipeline #${pipeline.id} for !${pipeline.merge_request.iid} with ${pipeline.merge_request.source_branch} into ${pipeline.merge_request.target_branch}`; const expected = `Pipeline #${mockPipelineWithAttachedMR.id} for !${mockPipelineWithAttachedMR.merge_request.iid} with ${mockPipelineWithAttachedMR.merge_request.source_branch} into ${mockPipelineWithAttachedMR.merge_request.target_branch}`;
const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText); const actual = trimText(findPipelineInfoText());
expect(actual).toBe(expected); expect(actual).toBe(expected);
}); });
it(`renders the correct merge request link`, () => { it(`renders the correct merge request link`, () => {
const actual = vm.$el.querySelector('.js-mr-link').href; expect(findMRLinkPath()).toBe(mockPipelineWithAttachedMR.merge_request.path);
expect(actual).toContain(pipeline.merge_request.path);
}); });
it(`renders the correct source branch link`, () => { it(`renders the correct source branch link`, () => {
const actual = vm.$el.querySelector('.js-source-branch-link').href; expect(findSourceBranchLinkPath()).toBe(
mockPipelineWithAttachedMR.merge_request.source_branch_path,
expect(actual).toContain(pipeline.merge_request.source_branch_path); );
}); });
it(`renders the correct target branch link`, () => { it(`renders the correct target branch link`, () => {
const actual = vm.$el.querySelector('.js-target-branch-link').href; expect(findTargetBranchLinkPath()).toBe(
mockPipelineWithAttachedMR.merge_request.target_branch_path,
expect(actual).toContain(pipeline.merge_request.target_branch_path); );
}); });
}); });
describe('with a detached merge request pipeline', () => { describe('with a detached merge request pipeline', () => {
let pipeline;
beforeEach(() => { beforeEach(() => {
pipeline = JSON.parse(JSON.stringify(mockPipelineData)); createComponent({
pipeline.flags.merge_request_pipeline = false; pipeline: mockPipelineDetached,
pipeline.flags.detached_merge_request_pipeline = true;
vm = mountComponent(Component, {
pipeline,
stages: [], stages: [],
selectedStage: 'deploy', selectedStage: 'deploy',
}); });
}); });
it(`renders the pipeline info like "Pipeline #123 for !456 with source_branch"`, () => { it(`renders the pipeline info like "Pipeline #123 for !456 with source_branch"`, () => {
const expected = `Pipeline #${pipeline.id} for !${pipeline.merge_request.iid} with ${pipeline.merge_request.source_branch}`; const expected = `Pipeline #${mockPipelineDetached.id} for !${mockPipelineDetached.merge_request.iid} with ${mockPipelineDetached.merge_request.source_branch}`;
const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText); const actual = trimText(findPipelineInfoText());
expect(actual).toBe(expected); expect(actual).toBe(expected);
}); });
it(`renders the correct merge request link`, () => { it(`renders the correct merge request link`, () => {
const actual = vm.$el.querySelector('.js-mr-link').href; expect(findMRLinkPath()).toBe(mockPipelineDetached.merge_request.path);
expect(actual).toContain(pipeline.merge_request.path);
}); });
it(`renders the correct source branch link`, () => { it(`renders the correct source branch link`, () => {
const actual = vm.$el.querySelector('.js-source-branch-link').href; expect(findSourceBranchLinkPath()).toBe(
mockPipelineDetached.merge_request.source_branch_path,
expect(actual).toContain(pipeline.merge_request.source_branch_path); );
}); });
}); });
}); });
...@@ -1189,3 +1189,86 @@ export const jobsInStage = { ...@@ -1189,3 +1189,86 @@ export const jobsInStage = {
path: '/gitlab-org/gitlab-shell/pipelines/27#build', path: '/gitlab-org/gitlab-shell/pipelines/27#build',
dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=build', dropdown_path: '/gitlab-org/gitlab-shell/pipelines/27/stage.json?stage=build',
}; };
export const mockPipelineWithoutMR = {
id: 28029444,
details: {
status: {
details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
group: 'success',
has_details: true,
icon: 'status_success',
label: 'passed',
text: 'passed',
tooltip: 'passed',
},
},
path: 'pipeline/28029444',
ref: {
name: 'test-branch',
},
};
export const mockPipelineWithAttachedMR = {
id: 28029444,
details: {
status: {
details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
group: 'success',
has_details: true,
icon: 'status_success',
label: 'passed',
text: 'passed',
tooltip: 'passed',
},
},
path: 'pipeline/28029444',
flags: {
merge_request_pipeline: true,
detached_merge_request_pipeline: false,
},
merge_request: {
iid: 1234,
path: '/root/detached-merge-request-pipelines/-/merge_requests/1',
title: 'Update README.md',
source_branch: 'feature-1234',
source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1234',
target_branch: 'master',
target_branch_path: '/root/detached-merge-request-pipelines/branches/master',
},
ref: {
name: 'test-branch',
},
};
export const mockPipelineDetached = {
id: 28029444,
details: {
status: {
details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
group: 'success',
has_details: true,
icon: 'status_success',
label: 'passed',
text: 'passed',
tooltip: 'passed',
},
},
path: 'pipeline/28029444',
flags: {
merge_request_pipeline: false,
detached_merge_request_pipeline: true,
},
merge_request: {
iid: 1234,
path: '/root/detached-merge-request-pipelines/-/merge_requests/1',
title: 'Update README.md',
source_branch: 'feature-1234',
source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1234',
target_branch: 'master',
target_branch_path: '/root/detached-merge-request-pipelines/branches/master',
},
ref: {
name: 'test-branch',
},
};
...@@ -68,11 +68,15 @@ RSpec.describe Gitlab::QueryLimiting::Transaction do ...@@ -68,11 +68,15 @@ RSpec.describe Gitlab::QueryLimiting::Transaction do
it 'increments the number of executed queries' do it 'increments the number of executed queries' do
transaction = described_class.new transaction = described_class.new
expect(transaction.count).to be_zero expect { transaction.increment }.to change { transaction.count }.by(1)
end
it 'does not increment the number of executed queries when query limiting is disabled' do
transaction = described_class.new
transaction.increment allow(transaction).to receive(:enabled?).and_return(false)
expect(transaction.count).to eq(1) expect { transaction.increment }.not_to change { transaction.count }
end end
end end
......
...@@ -2,81 +2,85 @@ ...@@ -2,81 +2,85 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::QueryLimiting do RSpec.describe Gitlab::QueryLimiting, :request_store do
describe '.enable?' do describe '.enabled_for_env?' do
it 'returns true in a test environment' do it 'returns true in a test environment' do
expect(described_class.enable?).to eq(true) expect(described_class.enabled_for_env?).to eq(true)
end end
it 'returns true in a development environment' do it 'returns true in a development environment' do
stub_rails_env('development') stub_rails_env('development')
stub_rails_env('development') stub_rails_env('development')
expect(described_class.enable?).to eq(true) expect(described_class.enabled_for_env?).to eq(true)
end end
it 'returns false on GitLab.com' do it 'returns false on GitLab.com' do
stub_rails_env('production') stub_rails_env('production')
allow(Gitlab).to receive(:com?).and_return(true) allow(Gitlab).to receive(:com?).and_return(true)
expect(described_class.enable?).to eq(false) expect(described_class.enabled_for_env?).to eq(false)
end end
it 'returns false in a non GitLab.com' do it 'returns false in a non GitLab.com' do
allow(Gitlab).to receive(:com?).and_return(false) allow(Gitlab).to receive(:com?).and_return(false)
stub_rails_env('production') stub_rails_env('production')
expect(described_class.enable?).to eq(false) expect(described_class.enabled_for_env?).to eq(false)
end end
end end
describe '.whitelist' do shared_context 'disable and enable' do |result|
it 'raises ArgumentError when an invalid issue URL is given' do let(:transaction) { Gitlab::QueryLimiting::Transaction.new }
expect { described_class.whitelist('foo') } let(:code) do
.to raise_error(ArgumentError) proc do
end 2.times { User.count }
context 'without a transaction' do
it 'does nothing' do
expect { described_class.whitelist('https://example.com') }
.not_to raise_error
end end
end end
context 'with a transaction' do
let(:transaction) { Gitlab::QueryLimiting::Transaction.new }
before do before do
allow(Gitlab::QueryLimiting::Transaction) allow(Gitlab::QueryLimiting::Transaction)
.to receive(:current) .to receive(:current)
.and_return(transaction) .and_return(transaction)
end end
end
it 'does not increment the number of SQL queries executed in the block' do describe '.disable!' do
before = transaction.count include_context 'disable and enable'
described_class.whitelist('https://example.com')
2.times do it 'raises an ArgumentError when an invalid issue URL is given' do
User.count expect { described_class.disable!('foo') }
.to raise_error(ArgumentError)
end end
expect(transaction.count).to eq(before) it 'stops the number of SQL queries from being incremented' do
described_class.disable!('https://example.com')
expect { code.call }.not_to change { transaction.count }
end
end end
it 'whitelists when enabled' do describe '.enable!' do
described_class.whitelist('https://example.com') include_context 'disable and enable'
expect(transaction.whitelisted).to eq(true) it 'allows the number of SQL queries to be incremented' do
end described_class.enable!
it 'does not whitelist when disabled' do expect { code.call }.to change { transaction.count }.by(2)
allow(described_class).to receive(:enable?).and_return(false) end
end
described_class.whitelist('https://example.com') describe '#enabled?' do
it 'returns true when enabled' do
Gitlab::SafeRequestStore[:query_limiting_disabled] = nil
expect(transaction.whitelisted).to eq(false) expect(described_class).to be_enabled
end end
it 'returns false when disabled' do
Gitlab::SafeRequestStore[:query_limiting_disabled] = true
expect(described_class).not_to be_enabled
end end
end end
end end
...@@ -90,6 +90,18 @@ RSpec.describe Ci::PipelineSchedule do ...@@ -90,6 +90,18 @@ RSpec.describe Ci::PipelineSchedule do
end end
end end
describe '.owned_by' do
let(:user) { create(:user) }
let!(:owned_pipeline_schedule) { create(:ci_pipeline_schedule, owner: user) }
let!(:other_pipeline_schedule) { create(:ci_pipeline_schedule) }
subject { described_class.owned_by(user) }
it 'returns owned pipeline schedules' do
is_expected.to eq([owned_pipeline_schedule])
end
end
describe '#set_next_run_at' do describe '#set_next_run_at' do
let(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly) } let(:pipeline_schedule) { create(:ci_pipeline_schedule, :nightly) }
let(:ideal_next_run_at) { pipeline_schedule.send(:ideal_next_run_from, Time.zone.now) } let(:ideal_next_run_at) { pipeline_schedule.send(:ideal_next_run_from, Time.zone.now) }
......
...@@ -1766,7 +1766,7 @@ RSpec.describe User do ...@@ -1766,7 +1766,7 @@ RSpec.describe User do
end end
describe 'blocking user' do describe 'blocking user' do
let(:user) { create(:user, name: 'John Smith') } let_it_be_with_refind(:user) { create(:user, name: 'John Smith') }
it 'blocks user' do it 'blocks user' do
user.block user.block
...@@ -1789,6 +1789,14 @@ RSpec.describe User do ...@@ -1789,6 +1789,14 @@ RSpec.describe User do
user.block user.block
end end
end end
context 'when user has active CI pipeline schedules' do
let_it_be(:schedule) { create(:ci_pipeline_schedule, active: true, owner: user) }
it 'disables any pipeline schedules' do
expect { user.block }.to change { schedule.reload.active? }.to(false)
end
end
end end
describe 'deactivating a user' do describe 'deactivating a user' do
......
...@@ -47,10 +47,10 @@ RSpec.describe 'getting merge request listings nested in a project' do ...@@ -47,10 +47,10 @@ RSpec.describe 'getting merge request listings nested in a project' do
end end
before do before do
# We cannot call the whitelist here, since the transaction does not # We cannot disable SQL query limiting here, since the transaction does not
# begin until we enter the controller. # begin until we enter the controller.
headers = { headers = {
'X-GITLAB-QUERY-WHITELIST-ISSUE' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/322979' 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/322979'
} }
post_graphql(query, current_user: current_user, headers: headers) post_graphql(query, current_user: current_user, headers: headers)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::DisableUserPipelineSchedulesService do
describe '#execute' do
let(:user) { create(:user) }
subject(:service) { described_class.new.execute(user) }
context 'when user has active pipeline schedules' do
let(:owned_pipeline_schedule) { create(:ci_pipeline_schedule, active: true, owner: user) }
it 'disables all active pipeline schedules', :aggregate_failures do
expect { service }.to change { owned_pipeline_schedule.reload.active? }
end
end
end
end
...@@ -359,6 +359,9 @@ RSpec.configure do |config| ...@@ -359,6 +359,9 @@ RSpec.configure do |config|
# Reset all feature flag stubs to default for testing # Reset all feature flag stubs to default for testing
stub_all_feature_flags stub_all_feature_flags
# Re-enable query limiting in case it was disabled
Gitlab::QueryLimiting.enable!
end end
config.before(:example, :mailer) do config.before(:example, :mailer) do
......
...@@ -15,20 +15,14 @@ end ...@@ -15,20 +15,14 @@ end
# If Sidekiq::Testing.inline! is used, SQL transactions done inside # If Sidekiq::Testing.inline! is used, SQL transactions done inside
# Sidekiq worker are included in the SQL query limit (in a real # Sidekiq worker are included in the SQL query limit (in a real
# deployment sidekiq worker is executed separately). To avoid # deployment sidekiq worker is executed separately). To avoid increasing
# increasing SQL limit counter, the request is marked as whitelisted # SQL limit counter, query limiting is disabled during Sidekiq block
# during Sidekiq block
class DisableQueryLimit class DisableQueryLimit
def call(worker_instance, msg, queue) def call(worker_instance, msg, queue)
transaction = Gitlab::QueryLimiting::Transaction.current ::Gitlab::QueryLimiting.disable!('https://mock-issue')
if !transaction.respond_to?(:whitelisted) || transaction.whitelisted
yield
else
transaction.whitelisted = true
yield yield
transaction.whitelisted = false ensure
end ::Gitlab::QueryLimiting.enable!
end end
end end
......
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