From 37a0f5e2cff68f10129a1cca7874b12e42827c0b Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 16 Sep 2022 12:11:31 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .rubocop_todo/rspec/verified_doubles.yml | 1 - .../jira_connect/subscriptions/api.js | 4 +- .../components/sign_in_oauth_button.vue | 4 +- app/assets/stylesheets/utilities.scss | 13 - .../admin/applications_controller.rb | 11 +- app/controllers/concerns/harbor/access.rb | 2 +- .../settings/applications_controller.rb | 12 +- .../oauth/applications_controller.rb | 11 +- app/helpers/jira_connect_helper.rb | 14 +- app/models/jira_connect_installation.rb | 6 + app/services/issues/base_service.rb | 3 - .../relative_position_rebalancing_service.rb | 2 - .../subscriptions/index.html.haml | 2 +- .../doorkeeper/applications/_show.html.haml | 9 +- .../development/hash_oauth_secrets.yml | 8 + .../development/rebalance_issues.yml | 8 - config/initializers/doorkeeper.rb | 4 +- config/initializers/zz_metrics.rb | 2 + .../20210216180242_web_ide_commits.yml | 5 + .../20210216180244_web_ide_views.yml | 5 + .../20210216180246_web_ide_merge_requests.yml | 5 + .../20210216180248_web_ide_previews.yml | 5 + .../20210216180250_web_ide_terminals.yml | 5 + .../20210216180252_web_ide_pipelines.yml | 5 + ...0220122022215_web_ide_previews_success.yml | 5 + .../20220831182105_add_constraints_view.rb | 32 ++ db/schema_migrations/20220831182105 | 1 + db/structure.sql | 15 + doc/api/discussions.md | 32 +- doc/development/documentation/index.md | 4 +- doc/development/import_project.md | 16 - doc/integration/oauth_provider.md | 10 + .../img/denied_licenses_v15_3.png | Bin 115891 -> 39570 bytes doc/user/group/epics/epic_boards.md | 36 ++ .../issues/img/related_issue_block_v15_3.png | Bin 28910 -> 10699 bytes .../issues/img/related_issues_add_v15_3.png | Bin 24947 -> 9286 bytes doc/user/project/issues/managing_issues.md | 7 +- .../ci/pipeline/chain/assign_partition.rb | 7 +- lib/gitlab/ci/pipeline/chain/command.rb | 4 + lib/gitlab/database/gitlab_schemas.yml | 1 + .../convert_table_to_first_list_partition.rb | 214 ++++++++++ .../table_management_helpers.rb | 48 +++ lib/gitlab/database/postgres_constraint.rb | 29 ++ .../pbkdf2_sha512.rb | 28 -- .../secret/pbkdf2_sha512.rb | 38 ++ .../token/pbkdf2_sha512.rb | 30 ++ .../importer/protected_branches_importer.rb | 2 +- .../import_export/base/relation_factory.rb | 2 + .../base/relation_object_saver.rb | 13 +- .../import_export/project/import_export.yml | 48 ++- .../import_export/project/object_builder.rb | 14 +- .../project/relation_tree_restorer.rb | 2 +- .../metrics/instrumentations/redis_metric.rb | 10 +- lib/gitlab/usage_data_counters.rb | 4 +- .../usage_data_counters/base_counter.rb | 4 +- .../menus/packages_registries_menu.rb | 2 +- locale/gitlab.pot | 6 + qa/qa/support/matchers/eventually_matcher.rb | 1 + .../admin/applications_controller_spec.rb | 84 +++- .../settings/applications_controller_spec.rb | 84 +++- .../oauth/applications_controller_spec.rb | 27 +- .../gitlab/import_export/group/project.json | 33 +- .../group/tree/project/issues.ndjson | 2 +- .../deprecated/gl-sast-report.json | 2 +- .../feature-branch/gl-sast-report.json | 2 +- .../gl-secret-detection-report.json | 2 +- .../gl-common-scanning-report-names.json | 2 +- ...ning-report-without-top-level-scanner.json | 45 +- .../master/gl-common-scanning-report.json | 403 +++++++++++------- .../master/gl-sast-missing-scanner.json | 2 +- .../master/gl-sast-report-bandit.json | 2 +- .../master/gl-sast-report-gosec.json | 2 +- .../master/gl-sast-report-minimal.json | 2 +- .../gl-sast-report-semgrep-for-bandit.json | 2 +- .../gl-sast-report-semgrep-for-gosec.json | 2 +- .../master/gl-sast-report.json | 2 +- .../master/gl-secret-detection-report.json | 2 +- .../components/sign_in_oauth_button_spec.js | 4 +- spec/helpers/jira_connect_helper_spec.rb | 32 +- .../pipelines/merge_requests_pipeline_spec.rb | 51 +++ .../pipeline/chain/assign_partition_spec.rb | 19 + .../gitlab/ci/pipeline/chain/command_spec.rb | 38 +- ...vert_table_to_first_list_partition_spec.rb | 246 +++++++++++ .../table_management_helpers_spec.rb | 70 +++ .../database/postgres_constraint_spec.rb | 123 ++++++ .../secret/pbkdf2_sha512_spec.rb | 45 ++ .../{ => token}/pbkdf2_sha512_spec.rb | 2 +- .../protected_branches_importer_spec.rb | 7 +- spec/lib/gitlab/import_export/all_models.yml | 14 + .../base/relation_object_saver_spec.rb | 25 +- .../import_export/safe_model_attributes.yml | 14 + .../instrumentations/redis_metric_spec.rb | 14 + spec/models/ci/runner_spec.rb | 143 +++---- spec/models/jira_connect_installation_spec.rb | 26 ++ spec/policies/project_policy_spec.rb | 35 ++ .../partitioning_spec.rb | 41 ++ ...ative_position_rebalancing_service_spec.rb | 15 - spec/services/issues/update_service_spec.rb | 32 -- spec/support/helpers/usage_data_helpers.rb | 4 - .../policies/project_policy_shared_context.rb | 13 +- .../manage_applications_shared_examples.rb | 112 +++-- .../project_policy_shared_examples.rb | 170 +++++--- ...applications_controller_shared_examples.rb | 6 + 103 files changed, 2228 insertions(+), 586 deletions(-) create mode 100644 config/feature_flags/development/hash_oauth_secrets.yml delete mode 100644 config/feature_flags/development/rebalance_issues.yml create mode 100644 db/migrate/20220831182105_add_constraints_view.rb create mode 100644 db/schema_migrations/20220831182105 create mode 100644 lib/gitlab/database/partitioning/convert_table_to_first_list_partition.rb create mode 100644 lib/gitlab/database/postgres_constraint.rb delete mode 100644 lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512.rb create mode 100644 lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512.rb create mode 100644 lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512.rb create mode 100644 spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb create mode 100644 spec/lib/gitlab/database/postgres_constraint_spec.rb create mode 100644 spec/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512_spec.rb rename spec/lib/gitlab/doorkeeper_secret_storing/{ => token}/pbkdf2_sha512_spec.rb (93%) diff --git a/.rubocop_todo/rspec/verified_doubles.yml b/.rubocop_todo/rspec/verified_doubles.yml index 0fa36d1941c..a225dbf21ae 100644 --- a/.rubocop_todo/rspec/verified_doubles.yml +++ b/.rubocop_todo/rspec/verified_doubles.yml @@ -448,7 +448,6 @@ RSpec/VerifiedDoubles: - spec/lib/gitlab/ci/config/external/file/project_spec.rb - spec/lib/gitlab/ci/config/external/rules_spec.rb - spec/lib/gitlab/ci/parsers/test/junit_spec.rb - - spec/lib/gitlab/ci/pipeline/chain/command_spec.rb - spec/lib/gitlab/ci/pipeline/chain/evaluate_workflow_rules_spec.rb - spec/lib/gitlab/ci/pipeline/chain/helpers_spec.rb - spec/lib/gitlab/ci/pipeline/chain/limit/deployments_spec.rb diff --git a/app/assets/javascripts/jira_connect/subscriptions/api.js b/app/assets/javascripts/jira_connect/subscriptions/api.js index 8b32a67c23b..c79d7002111 100644 --- a/app/assets/javascripts/jira_connect/subscriptions/api.js +++ b/app/assets/javascripts/jira_connect/subscriptions/api.js @@ -92,6 +92,6 @@ export const fetchOAuthApplicationId = () => { return axiosInstance.get(JIRA_CONNECT_OAUTH_APPLICATION_ID_PATH); }; -export const fetchOAuthToken = (oauthTokenURL, data = {}) => { - return axiosInstance.post(oauthTokenURL, data); +export const fetchOAuthToken = (oauthTokenPath, data = {}) => { + return axiosInstance.post(oauthTokenPath, data); }; diff --git a/app/assets/javascripts/jira_connect/subscriptions/components/sign_in_oauth_button.vue b/app/assets/javascripts/jira_connect/subscriptions/components/sign_in_oauth_button.vue index 8df906dd292..4cf3a1a0279 100644 --- a/app/assets/javascripts/jira_connect/subscriptions/components/sign_in_oauth_button.vue +++ b/app/assets/javascripts/jira_connect/subscriptions/components/sign_in_oauth_button.vue @@ -159,9 +159,9 @@ export default { async getOAuthToken(code) { const { oauth_token_payload: oauthTokenPayload, - oauth_token_url: oauthTokenURL, + oauth_token_path: oauthTokenPath, } = this.oauthMetadata; - const { data } = await fetchOAuthToken(oauthTokenURL, { + const { data } = await fetchOAuthToken(oauthTokenPath, { ...oauthTokenPayload, code, code_verifier: this.codeVerifier, diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss index e5b6c9811b5..bdb8f758137 100644 --- a/app/assets/stylesheets/utilities.scss +++ b/app/assets/stylesheets/utilities.scss @@ -285,19 +285,6 @@ $gl-line-height-42: px-to-rem(42px); padding-right: $gl-spacing-scale-10; } -// TODO: will be moved to @gitlab/ui as part of https://gitlab.com/gitlab-org/gitlab/-/issues/349008 -.gl-sm-mt-6 { - @include media-breakpoint-up(sm) { - margin-top: $gl-spacing-scale-6; - } -} - -.gl-sm-mt-6\! { - @include media-breakpoint-up(sm) { - margin-top: $gl-spacing-scale-6 !important; - } -} - /* All of the following (up until the "End gitlab-ui#1709" comment) will be moved to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1709 diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb index b0d7c8cb8f2..d66b3cb4366 100644 --- a/app/controllers/admin/applications_controller.rb +++ b/app/controllers/admin/applications_controller.rb @@ -14,7 +14,7 @@ class Admin::ApplicationsController < Admin::ApplicationController end def show - @created = get_created_session + @created = get_created_session if Feature.disabled?('hash_oauth_secrets') end def new @@ -30,9 +30,14 @@ class Admin::ApplicationsController < Admin::ApplicationController if @application.persisted? flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) - set_created_session + if Feature.enabled?('hash_oauth_secrets') + @created = true + render :show + else + set_created_session - redirect_to admin_application_url(@application) + redirect_to admin_application_url(@application) + end else render :new end diff --git a/app/controllers/concerns/harbor/access.rb b/app/controllers/concerns/harbor/access.rb index 70de72f15fc..211566aeda7 100644 --- a/app/controllers/concerns/harbor/access.rb +++ b/app/controllers/concerns/harbor/access.rb @@ -17,7 +17,7 @@ module Harbor private def harbor_registry_enabled! - render_404 unless Feature.enabled?(:harbor_registry_integration) + render_404 unless Feature.enabled?(:harbor_registry_integration, defined?(group) ? group : project) end def authorize_read_harbor_registry! diff --git a/app/controllers/groups/settings/applications_controller.rb b/app/controllers/groups/settings/applications_controller.rb index bfe61696e0f..3557d485422 100644 --- a/app/controllers/groups/settings/applications_controller.rb +++ b/app/controllers/groups/settings/applications_controller.rb @@ -16,7 +16,7 @@ module Groups end def show - @created = get_created_session + @created = get_created_session if Feature.disabled?('hash_oauth_secrets') end def edit @@ -28,9 +28,15 @@ module Groups if @application.persisted? flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) - set_created_session + if Feature.enabled?('hash_oauth_secrets') - redirect_to group_settings_application_url(@group, @application) + @created = true + render :show + else + set_created_session + + redirect_to group_settings_application_url(@group, @application) + end else set_index_vars render :index diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index a996bad3fac..ff466fd5fbb 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -25,7 +25,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController end def show - @created = get_created_session + @created = get_created_session if Feature.disabled?('hash_oauth_secrets') end def create @@ -34,9 +34,14 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController if @application.persisted? flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) - set_created_session + if Feature.enabled?('hash_oauth_secrets') + @created = true + render :show + else + set_created_session - redirect_to oauth_application_url(@application) + redirect_to oauth_application_url(@application) + end else set_index_vars render :index diff --git a/app/helpers/jira_connect_helper.rb b/app/helpers/jira_connect_helper.rb index 4ddfb0224d1..0971fdae8dd 100644 --- a/app/helpers/jira_connect_helper.rb +++ b/app/helpers/jira_connect_helper.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module JiraConnectHelper - def jira_connect_app_data(subscriptions) + def jira_connect_app_data(subscriptions, installation) skip_groups = subscriptions.map(&:namespace_id) { @@ -11,14 +11,16 @@ module JiraConnectHelper subscriptions_path: jira_connect_subscriptions_path(format: :json), users_path: current_user ? nil : jira_connect_users_path, # users_path is used to determine if user is signed in gitlab_user_path: current_user ? user_path(current_user) : nil, - oauth_metadata: Feature.enabled?(:jira_connect_oauth, current_user) ? jira_connect_oauth_data.to_json : nil + oauth_metadata: Feature.enabled?(:jira_connect_oauth, current_user) ? jira_connect_oauth_data(installation).to_json : nil } end private - def jira_connect_oauth_data - oauth_authorize_url = oauth_authorization_url( + def jira_connect_oauth_data(installation) + oauth_instance_url = installation.oauth_authorization_url + + oauth_authorize_path = oauth_authorization_path( client_id: Gitlab::CurrentSettings.jira_connect_application_key, response_type: 'code', scope: 'api', @@ -27,8 +29,8 @@ module JiraConnectHelper ) { - oauth_authorize_url: oauth_authorize_url, - oauth_token_url: oauth_token_url, + oauth_authorize_url: Gitlab::Utils.append_path(oauth_instance_url, oauth_authorize_path), + oauth_token_path: oauth_token_path, state: oauth_state, oauth_token_payload: { grant_type: :authorization_code, diff --git a/app/models/jira_connect_installation.rb b/app/models/jira_connect_installation.rb index 8befe9a9230..0a2d3ba0749 100644 --- a/app/models/jira_connect_installation.rb +++ b/app/models/jira_connect_installation.rb @@ -24,4 +24,10 @@ class JiraConnectInstallation < ApplicationRecord def client Atlassian::JiraConnect::Client.new(base_url, shared_secret) end + + def oauth_authorization_url + return Gitlab.config.gitlab.url if instance_url.blank? || Feature.disabled?(:jira_connect_oauth_self_managed) + + instance_url + end end diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 61a95e49228..d75e74f3b19 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -28,9 +28,6 @@ module Issues return if issue.relative_position.nil? return if NO_REBALANCING_NEEDED.cover?(issue.relative_position) - gates = [issue.project, issue.project.group].compact - return unless gates.any? { |gate| Feature.enabled?(:rebalance_issues, gate) } - Issues::RebalancingWorker.perform_async(nil, *issue.project.self_or_root_group_ids) end diff --git a/app/services/issues/relative_position_rebalancing_service.rb b/app/services/issues/relative_position_rebalancing_service.rb index 23bb409f3cd..b5c10430e83 100644 --- a/app/services/issues/relative_position_rebalancing_service.rb +++ b/app/services/issues/relative_position_rebalancing_service.rb @@ -16,8 +16,6 @@ module Issues end def execute - return unless Feature.enabled?(:rebalance_issues, root_namespace) - # Given can_start_rebalance? and track_new_running_rebalance are not atomic # it can happen that we end up with more than Rebalancing::State::MAX_NUMBER_OF_CONCURRENT_REBALANCES running. # Considering the number of allowed Rebalancing::State::MAX_NUMBER_OF_CONCURRENT_REBALANCES is small we should be ok, diff --git a/app/views/jira_connect/subscriptions/index.html.haml b/app/views/jira_connect/subscriptions/index.html.haml index d4ced15b869..f66aa0840aa 100644 --- a/app/views/jira_connect/subscriptions/index.html.haml +++ b/app/views/jira_connect/subscriptions/index.html.haml @@ -1,4 +1,4 @@ -.js-jira-connect-app{ data: jira_connect_app_data(@subscriptions) } +.js-jira-connect-app{ data: jira_connect_app_data(@subscriptions, @current_jira_installation) } = webpack_bundle_tag 'performance_bar' if performance_bar_enabled? = webpack_bundle_tag 'jira_connect_app' diff --git a/app/views/shared/doorkeeper/applications/_show.html.haml b/app/views/shared/doorkeeper/applications/_show.html.haml index f533b5b5a4d..562b1aee4ca 100644 --- a/app/views/shared/doorkeeper/applications/_show.html.haml +++ b/app/views/shared/doorkeeper/applications/_show.html.haml @@ -15,7 +15,14 @@ %td = _('Secret') %td - = clipboard_button(clipboard_text: @application.secret, button_text: _('Copy'), title: _("Copy secret"), class: "btn btn-default btn-md gl-button") + - if Feature.enabled?('hash_oauth_secrets') + - if @application.plaintext_secret + = clipboard_button(clipboard_text: @application.plaintext_secret, button_text: _('Copy'), title: _("Copy secret"), class: "btn btn-default btn-md gl-button") + %span= _('This is the only time the secret is accessible. Copy the secret and store it securely.') + - else + = _('The secret is only available when you first create the application.') + - else + = clipboard_button(clipboard_text: @application.secret, button_text: _('Copy'), title: _("Copy secret"), class: "btn btn-default btn-md gl-button") %tr %td = _('Callback URL') diff --git a/config/feature_flags/development/hash_oauth_secrets.yml b/config/feature_flags/development/hash_oauth_secrets.yml new file mode 100644 index 00000000000..7730d319bab --- /dev/null +++ b/config/feature_flags/development/hash_oauth_secrets.yml @@ -0,0 +1,8 @@ +--- +name: hash_oauth_secrets +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96252 +rollout_issue_url: +milestone: '15.4' +type: development +group: group::authentication and authorization +default_enabled: false diff --git a/config/feature_flags/development/rebalance_issues.yml b/config/feature_flags/development/rebalance_issues.yml deleted file mode 100644 index 5651b02b073..00000000000 --- a/config/feature_flags/development/rebalance_issues.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: rebalance_issues -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40124 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/239344 -milestone: '13.4' -type: development -group: group::project management -default_enabled: true diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 867f3fd47cc..918b2767c4d 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -90,7 +90,9 @@ Doorkeeper.configure do # Check out the wiki for more information on customization access_token_methods :from_access_token_param, :from_bearer_authorization, :from_bearer_param - hash_token_secrets using: '::Gitlab::DoorkeeperSecretStoring::Pbkdf2Sha512', fallback: :plain + hash_token_secrets using: '::Gitlab::DoorkeeperSecretStoring::Token::Pbkdf2Sha512', fallback: :plain + + hash_application_secrets using: '::Gitlab::DoorkeeperSecretStoring::Secret::Pbkdf2Sha512', fallback: :plain # Specify what grant flows are enabled in array of Strings. The valid # strings and the flows they enable are: diff --git a/config/initializers/zz_metrics.rb b/config/initializers/zz_metrics.rb index 3a729e34135..940d8eed61f 100644 --- a/config/initializers/zz_metrics.rb +++ b/config/initializers/zz_metrics.rb @@ -40,6 +40,8 @@ if Gitlab::Metrics.enabled? && !Rails.env.test? && !(Rails.env.development? && d if Gitlab::Runtime.puma? Gitlab::Metrics::RequestsRackMiddleware.initialize_metrics Gitlab::Metrics::GlobalSearchSlis.initialize_slis! + elsif Gitlab.ee? && Gitlab::Runtime.sidekiq? + Gitlab::Metrics::GlobalSearchIndexingSlis.initialize_slis! end GC::Profiler.enable diff --git a/config/metrics/counts_all/20210216180242_web_ide_commits.yml b/config/metrics/counts_all/20210216180242_web_ide_commits.yml index 3d1d416a7c2..f86b5bd5f84 100644 --- a/config/metrics/counts_all/20210216180242_web_ide_commits.yml +++ b/config/metrics/counts_all/20210216180242_web_ide_commits.yml @@ -10,6 +10,11 @@ value_type: number status: active time_frame: all data_source: redis +instrumentation_class: RedisMetric +options: + prefix: web_ide + event: commits_count + include_usage_prefix: false distribution: - ce - ee diff --git a/config/metrics/counts_all/20210216180244_web_ide_views.yml b/config/metrics/counts_all/20210216180244_web_ide_views.yml index 7bc32b3dbc9..63149b86e0f 100644 --- a/config/metrics/counts_all/20210216180244_web_ide_views.yml +++ b/config/metrics/counts_all/20210216180244_web_ide_views.yml @@ -10,6 +10,11 @@ value_type: number status: active time_frame: all data_source: redis +instrumentation_class: RedisMetric +options: + prefix: web_ide + event: views_count + include_usage_prefix: false distribution: - ce - ee diff --git a/config/metrics/counts_all/20210216180246_web_ide_merge_requests.yml b/config/metrics/counts_all/20210216180246_web_ide_merge_requests.yml index eb02d98dc85..f620447e615 100644 --- a/config/metrics/counts_all/20210216180246_web_ide_merge_requests.yml +++ b/config/metrics/counts_all/20210216180246_web_ide_merge_requests.yml @@ -10,6 +10,11 @@ value_type: number status: active time_frame: all data_source: redis +instrumentation_class: RedisMetric +options: + prefix: web_ide + event: merge_requests_count + include_usage_prefix: false distribution: - ce - ee diff --git a/config/metrics/counts_all/20210216180248_web_ide_previews.yml b/config/metrics/counts_all/20210216180248_web_ide_previews.yml index 4d581fc7f7e..c785e95e105 100644 --- a/config/metrics/counts_all/20210216180248_web_ide_previews.yml +++ b/config/metrics/counts_all/20210216180248_web_ide_previews.yml @@ -10,6 +10,11 @@ value_type: number status: active time_frame: all data_source: redis +instrumentation_class: RedisMetric +options: + prefix: web_ide + event: previews_count + include_usage_prefix: false distribution: - ce - ee diff --git a/config/metrics/counts_all/20210216180250_web_ide_terminals.yml b/config/metrics/counts_all/20210216180250_web_ide_terminals.yml index e8c1f425639..cd64a877341 100644 --- a/config/metrics/counts_all/20210216180250_web_ide_terminals.yml +++ b/config/metrics/counts_all/20210216180250_web_ide_terminals.yml @@ -10,6 +10,11 @@ value_type: number status: active time_frame: all data_source: redis +instrumentation_class: RedisMetric +options: + prefix: web_ide + event: terminals_count + include_usage_prefix: false distribution: - ce - ee diff --git a/config/metrics/counts_all/20210216180252_web_ide_pipelines.yml b/config/metrics/counts_all/20210216180252_web_ide_pipelines.yml index ae891775bf9..bfd0b69401e 100644 --- a/config/metrics/counts_all/20210216180252_web_ide_pipelines.yml +++ b/config/metrics/counts_all/20210216180252_web_ide_pipelines.yml @@ -10,6 +10,11 @@ value_type: number status: active time_frame: all data_source: redis +instrumentation_class: RedisMetric +options: + prefix: web_ide + event: pipelines_count + include_usage_prefix: false distribution: - ce - ee diff --git a/config/metrics/counts_all/20220122022215_web_ide_previews_success.yml b/config/metrics/counts_all/20220122022215_web_ide_previews_success.yml index 203201e9174..e2d617dba16 100644 --- a/config/metrics/counts_all/20220122022215_web_ide_previews_success.yml +++ b/config/metrics/counts_all/20220122022215_web_ide_previews_success.yml @@ -10,6 +10,11 @@ value_type: number status: active time_frame: all data_source: redis +instrumentation_class: RedisMetric +options: + prefix: web_ide + event: previews_success_count + include_usage_prefix: false distribution: - ce - ee diff --git a/db/migrate/20220831182105_add_constraints_view.rb b/db/migrate/20220831182105_add_constraints_view.rb new file mode 100644 index 00000000000..03c183b6e9f --- /dev/null +++ b/db/migrate/20220831182105_add_constraints_view.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class AddConstraintsView < Gitlab::Database::Migration[2.0] + def up + execute(<<~SQL) + CREATE OR REPLACE VIEW postgres_constraints + AS + SELECT + pg_constraint.oid AS oid, + pg_constraint.conname AS name, + pg_constraint.contype AS constraint_type, + pg_constraint.convalidated AS constraint_valid, + (SELECT array_agg(attname ORDER BY ordering) + FROM unnest(pg_constraint.conkey) WITH ORDINALITY attnums(attnum, ordering) + INNER JOIN pg_attribute ON pg_attribute.attnum = attnums.attnum AND pg_attribute.attrelid = pg_class.oid + ) AS column_names, + pg_namespace.nspname::text || '.'::text || pg_class.relname::text AS table_identifier, + -- pg_constraint reports a 0 oid rather than null if the constraint is not a partition child constraint. + nullif(pg_constraint.conparentid, 0) AS parent_constraint_oid, + pg_get_constraintdef(pg_constraint.oid) AS definition + FROM pg_constraint + INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid + INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid; + SQL + end + + def down + execute(<<~SQL) + DROP VIEW postgres_constraints; + SQL + end +end diff --git a/db/schema_migrations/20220831182105 b/db/schema_migrations/20220831182105 new file mode 100644 index 00000000000..6f4b0f46ff1 --- /dev/null +++ b/db/schema_migrations/20220831182105 @@ -0,0 +1 @@ +80828666cac381dde65dc208764b6e1c7fe703b63c708410f72afdd33886fc60 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index f536bd170eb..d03a291ea6b 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -19307,6 +19307,21 @@ CREATE VIEW postgres_autovacuum_activity AS COMMENT ON VIEW postgres_autovacuum_activity IS 'Contains information about PostgreSQL backends currently performing autovacuum operations on the tables indicated here.'; +CREATE VIEW postgres_constraints AS + SELECT pg_constraint.oid, + pg_constraint.conname AS name, + pg_constraint.contype AS constraint_type, + pg_constraint.convalidated AS constraint_valid, + ( SELECT array_agg(pg_attribute.attname ORDER BY attnums.ordering) AS array_agg + FROM (unnest(pg_constraint.conkey) WITH ORDINALITY attnums(attnum, ordering) + JOIN pg_attribute ON (((pg_attribute.attnum = attnums.attnum) AND (pg_attribute.attrelid = pg_class.oid))))) AS column_names, + (((pg_namespace.nspname)::text || '.'::text) || (pg_class.relname)::text) AS table_identifier, + NULLIF(pg_constraint.conparentid, (0)::oid) AS parent_constraint_oid, + pg_get_constraintdef(pg_constraint.oid) AS definition + FROM ((pg_constraint + JOIN pg_class ON ((pg_constraint.conrelid = pg_class.oid))) + JOIN pg_namespace ON ((pg_class.relnamespace = pg_namespace.oid))); + CREATE VIEW postgres_foreign_keys AS SELECT pg_constraint.oid, pg_constraint.conname AS name, diff --git a/doc/api/discussions.md b/doc/api/discussions.md index a5610adad79..60ce10590e1 100644 --- a/doc/api/discussions.md +++ b/doc/api/discussions.md @@ -1109,7 +1109,7 @@ GET /projects/:id/repository/commits/:commit_id/discussions | Attribute | Type | Required | Description | | ------------------- | ---------------- | ---------- | ------------ | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) | -| `commit_id` | integer | yes | The ID of a commit | +| `commit_id` | string | yes | The SHA of a commit | ```json [ @@ -1237,7 +1237,7 @@ Diff comments contain also position: ```shell curl --header "PRIVATE-TOKEN: "\ - "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions" + "https://gitlab.example.com/api/v4/projects/5/repository/commits//discussions" ``` ### Get single commit discussion item @@ -1253,12 +1253,12 @@ Parameters: | Attribute | Type | Required | Description | | ------------------- | -------------- | -------- | ----------- | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) | -| `commit_id` | integer | yes | The ID of a commit | -| `discussion_id` | integer | yes | The ID of a discussion item | +| `commit_id` | string | yes | The SHA of a commit | +| `discussion_id` | string | yes | The ID of a discussion item | ```shell curl --header "PRIVATE-TOKEN: "\ - "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions/" + "https://gitlab.example.com/api/v4/projects/5/repository/commits//discussions/" ``` ### Create new commit thread @@ -1294,7 +1294,7 @@ Parameters: ```shell curl --request POST --header "PRIVATE-TOKEN: "\ - "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions?body=comment" + "https://gitlab.example.com/api/v4/projects/5/repository/commits//discussions?body=comment" ``` The rules for creating the API request are the same as when @@ -1314,15 +1314,15 @@ Parameters: | Attribute | Type | Required | Description | | ------------------- | -------------- | -------- | ----------- | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) | -| `commit_id` | integer | yes | The ID of a commit | -| `discussion_id` | integer | yes | The ID of a thread | +| `commit_id` | string | yes | The SHA of a commit | +| `discussion_id` | string | yes | The ID of a thread | | `note_id` | integer | yes | The ID of a thread note | | `body` | string | yes | The content of the note/reply | | `created_at` | string | no | Date time string, ISO 8601 formatted, such `2016-03-11T03:45:40Z` (requires administrator or project/group owner rights) | ```shell curl --request POST --header "PRIVATE-TOKEN: "\ - "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions//notes?body=comment + "https://gitlab.example.com/api/v4/projects/5/repository/commits//discussions//notes?body=comment ``` ### Modify an existing commit thread note @@ -1338,21 +1338,21 @@ Parameters: | Attribute | Type | Required | Description | | ------------------- | -------------- | -------- | ----------- | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) | -| `commit_id` | integer | yes | The ID of a commit | -| `discussion_id` | integer | yes | The ID of a thread | +| `commit_id` | string | yes | The SHA of a commit | +| `discussion_id` | string | yes | The ID of a thread | | `note_id` | integer | yes | The ID of a thread note | | `body` | string | no | The content of a note | ```shell curl --request PUT --header "PRIVATE-TOKEN: "\ - "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions//notes/1108?body=comment" + "https://gitlab.example.com/api/v4/projects/5/repository/commits//discussions//notes/1108?body=comment" ``` Resolving a note: ```shell curl --request PUT --header "PRIVATE-TOKEN: "\ - "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions//notes/1108?resolved=true" + "https://gitlab.example.com/api/v4/projects/5/repository/commits//discussions//notes/1108?resolved=true" ``` ### Delete a commit thread note @@ -1368,11 +1368,11 @@ Parameters: | Attribute | Type | Required | Description | | ------------------- | -------------- | -------- | ----------- | | `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) | -| `commit_id` | integer | yes | The ID of a commit | -| `discussion_id` | integer | yes | The ID of a thread | +| `commit_id` | string | yes | The SHA of a commit | +| `discussion_id` | string | yes | The ID of a thread | | `note_id` | integer | yes | The ID of a thread note | ```shell curl --request DELETE --header "PRIVATE-TOKEN: "\ - "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions/636" + "https://gitlab.example.com/api/v4/projects/5/repository/commits//discussions//notes/636" ``` diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index f4e74973fd7..73c1874f09e 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -240,9 +240,9 @@ Every GitLab instance includes documentation at `/help` (`https://gitlab.example that matches the version of the instance. For example, . The documentation available online at is deployed every -four hours from the default branch of [GitLab, Omnibus, Runner, and Charts](#source-files-and-rendered-web-locations). +hour from the default branch of [GitLab, Omnibus, Runner, and Charts](#source-files-and-rendered-web-locations). After a merge request that updates documentation is merged, it is available online -in 4 hours or less. +in an hour or less. However, it's only available at `/help` on self-managed instances in the next released version. The date an update is merged can impact which self-managed release the update diff --git a/doc/development/import_project.md b/doc/development/import_project.md index 7c55d2e2668..1f3bf860257 100644 --- a/doc/development/import_project.md +++ b/doc/development/import_project.md @@ -149,26 +149,10 @@ You might see an error like `N is out of range for ActiveModel::Type::Integer wi where `N` is the integer exceeding the 4-byte integer limit. If that's the case, you are likely hitting the issue with rebalancing of `relative_position` field of the issues. -The feature flag to enable the rebalance automatically was enabled on GitLab.com. -We intend to enable it by default on self-managed instances when the issue -[Rebalance issues FF rollout](https://gitlab.com/gitlab-org/gitlab/-/issues/343368) -is implemented. - -If the feature is not enabled by default on your GitLab version, run the following -commands in the [Rails console](../administration/operations/rails_console.md) as -a workaround. Replace the ID with the ID of your project you were trying to import: - ```ruby -# Check if the feature is enabled on your instance. If it is, rebalance should work automatically on your instance -Feature.enabled?(:rebalance_issues,Project.find(ID).root_namespace) - # Check the current maximum value of relative_position Issue.where(project_id: Project.find(ID).root_namespace.all_projects).maximum(:relative_position) -# Enable `rebalance_issues` feauture and check that it was successfully enabled -Feature.enable(:rebalance_issues,Project.find(ID).root_namespace) -Feature.enabled?(:rebalance_issues,Project.find(ID).root_namespace) - # Run the rebalancing process and check if the maximum value of relative_position has changed Issues::RelativePositionRebalancingService.new(Project.find(ID).root_namespace.all_projects).execute Issue.where(project_id: Project.find(ID).root_namespace.all_projects).maximum(:relative_position) diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md index 55a14cb4859..21184f7b678 100644 --- a/doc/integration/oauth_provider.md +++ b/doc/integration/oauth_provider.md @@ -127,3 +127,13 @@ application can perform. Available scopes are depicted in the following table. | `email` | Grants read-only access to the user's primary email address using [OpenID Connect](openid_connect_provider.md). | At any time you can revoke any access by clicking **Revoke**. + +## Hashed OAuth application secrets + +> Introduced in GitLab 15.4 [with a flag](../administration/feature_flags.md) named `hash_oauth_secrets`. Disabled by default. + +FLAG: +On self-managed GitLab, by default, this feature is not available. To make it available, ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `hash_oauth_secrets`. +On GitLab.com, this feature is not available. + +By default, OAuth application secrets are stored as plain text in the database. When enabled, OAuth application secrets are stored in the database in hashed format and are only available to users immediately after creating OAuth applications. diff --git a/doc/user/compliance/license_compliance/img/denied_licenses_v15_3.png b/doc/user/compliance/license_compliance/img/denied_licenses_v15_3.png index db29cc1cf13b4bddf5d9cfa57367526cef9b2c0f..4ed8404713306661b859f6546a49f3f63be9b814 100644 GIT binary patch delta 39101 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

ut#`AsD$H0@w383 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 aoB<-R-gxMt2&@1A00{s|MNUMnLSTXhO@_t* literal 115891 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%0A3*;AfK>RbAOHIZ0d*b2 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 z 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 Zgw>Xn{6lk9)+yklt)YJtp=K5G{{SuX?qdJ| diff --git a/doc/user/group/epics/epic_boards.md b/doc/user/group/epics/epic_boards.md index 25cb88e8a7c..a8bbb0575b3 100644 --- a/doc/user/group/epics/epic_boards.md +++ b/doc/user/group/epics/epic_boards.md @@ -155,6 +155,42 @@ into another list. Learn about possible effects in [Dragging epics between lists To move a list, select its top bar, and drag it horizontally. You can't move the **Open** and **Closed** lists, but you can hide them when editing an epic board. +#### Move an epic to the start of the list + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367473) in GitLab 15.4. + +When you have many epics, it's inconvenient to manually drag an epic from the bottom of a board list all +the way to the top. You can move epics to the top of the list with a menu shortcut. + +Your epic is moved to the top of the list even if other epics are hidden by a filter. + +Prerequisites: + +- You must at least have the Reporter role for a group. + +To move an epic to the start of the list: + +1. In an epic board, hover over the card of the epic you want to move. +1. Select the vertical ellipsis (**{ellipsis_v}**), then **Move to start of list**. + +#### Move an epic to the end of the list + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367473) in GitLab 15.4. + +When you have many epics, it's inconvenient to manually drag an epic from the top of a board list all +the way to the bottom. You can move epics to the bottom of the list with a menu shortcut. + +Your epic is moved to the bottom of the list even if other epics are hidden by a filter. + +Prerequisites: + +- You must at least have the Reporter role for a group. + +To move an epic to the end of the list: + +1. In an epic board, hover over the card of the epic you want to move. +1. Select the vertical ellipsis (**{ellipsis_v}**), then **Move to end of list**. + #### Dragging epics between lists When you drag epics between lists, the result is different depending on the source list diff --git a/doc/user/project/issues/img/related_issue_block_v15_3.png b/doc/user/project/issues/img/related_issue_block_v15_3.png index 827ddeabf1034cf98bf037adc23651ca574274b5..942f7a33fe0418aa3de172a7d0fa07733f256796 100644 GIT binary patch literal 10699 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 aJX=b3hBKX?KENzOBv~n?x1|yW{{I5vDbwKq diff --git a/doc/user/project/issues/img/related_issues_add_v15_3.png b/doc/user/project/issues/img/related_issues_add_v15_3.png index 7c6edf6142705851efda56f98d39a3b778778277..28739c0b9096d0053216005c571131d17510971a 100644 GIT binary patch literal 9286 zcmY+q2RNJ2+dm#PYinzjq61aL-fFhARa9-FYF81lsZH!zd(T=$loqiyirQO>7_nz! zZ}Lm~{l4%2{pY%#JlAuc`<(kePwxBk`J9|5;*EwX75QCq002Ps^2M{a001Ez03diz zMu=}IrsuqYKQX&LH*nQGhoYMehp?cqFo%$^w4kK4sPK(*o*#HWfH$vop5t)1v$HeY zHSX%_N+}NY_U&7Cx4xsJBMb&JGc$8>adCKfczSv|J3A{VDA?BCZf$K%`=L!xNE~-L zc6oUvE+klAU+*L#+~6i9?b$qjiIbNUT3%kRgjJoK93vC7A|fJUG{n1qcUu*qGcz+2 z6Osfa=!bfoEf&)ygs7B28_I9eCFb=nk!!3$ERl%REOSYGatg&02*zxrI zclcZTvnbR~#mBZSEI#D4v~*Fc5=7v0<-l&wma8)yj`vf4l)B*6PRDC`@tS?y`ue)K zqQ9tFo-2GG<{&XVIy!lHdVI7LswN_)q%A1?2Ded$!JwT==MWdTjLAJg5kX-|2m}I_ z*RZmABX2Dt7}JOKR~8WmSMB`_R8V@VuPQZ-eA`cwFCu~#Kpw*#oyw3 zpvix5#l2hW7;O5_b?omrMR9#)K|z!3*^g54#YM$k7+iq4dXbH|ef940HLiZ@c=Pb0 z)J*W={QRwBTo3eFKo4&28i(B;6Mg$>c$R#G!J{Rac>G^$>T~c)CBRpEe#* zzZB4l-N)hNUT7(cKEG&k7gDr*EiNpk5&F9w7FEzBs2VUoKVJd;U?e8Y?mhh`Y3f(? z$XvSa!Px}{vyMCKh9}rBcV(BLaNTQ%?ymlaQ$J({1s#%Vu!AK-(XX4Tzbs-fLqmOf zhWaL+d7b$_%I^Z8#!prB9JlM-HKTiQm}+#ngOY^VhscJnGkeWbTYCQ0iJ$vRH*oP_ z&4PyCyK`UM@&;;Gcay7oUOVSiH2sVlyKHW5;7r}fn8)qUbzGmUDY({z{XRt^Cu)4u zrt&SU<%P}7%>ye()BE?@2gb0gHKjg=F%b72cusd!ba0^e)p)|}62_x?RYBU(J|wrg zW5yxvw`lM%vt!p4x>i1NVt5?=;(bE?3eLgyLwEW|1Krn<$gXF#2S=!RV`#_b)&A+( z^-|wxMOsW*`Q+pds;dDJ(^R&0(;M7^Pg|N=_p#3y@4iwqs@~g zN+oMFBklph3vQA?6kvtHsKGJ-VqMEH#|#E8@zbtafHB!< z6{l)|888w;fFM?Y1IQTH@3&F*+=o&e<{@5=D5mNBnY)j^(b8|^**{T#p#NBIL0azx zT;WgJGC8KR6ZR5H0gWnNZ~Yl^p&*LBVfIk^&&k1|4}4NHQLfcurW48Neif&2-Crz| z)=veHFM{x2NHF&)&un>%qJBDpS*&1;aX&QSO97I533UcKF0C)FjwF6wMZ8>Dc{y@z zOzoFaswTa@$PkeG2A%`=wBU4np`;15o0U|F`!QdTa+Vr=^W>pW;euxD-jK)jSQr;` zq21{4u|wT6i}Vk?nup*zwy(x-_AyVuwBc}w2)CT!ZZL%Z=^fZ6l|H{*>($wNgfFHo zbG$hZkvyBi&6(WiOc;`B&MV=U8pahxOnm|P*Kur3k7j*^PX8WjW}Vqm^WBn*UrJ9= zF}%OF3!)CP3>{hD=sHLZlGT5F`B5uJnM_K~`=s|fD`ui<60Vu0HzAcwQ8T!%tJ>#X zqg+@J+Us3ALV(aUA&tpXm3$j@?c@1^uAc0f0X!#E4X2TuZp5GqEG_+v_A^=IE2De@ zvU6|W@uA%##|vC! zb_T>!E9M9!My$R!+`0+_wL!vCO4j`Y#S=8M)7>+Si5~Ew7?GwqjoUQ^D3q7ndDn!e z6?XYH*8kl44LY{fouAY7A?$cw-h9n81wEb8FIlM`5yh4vYjr-5@?&FC5(lX*t7*`e zvC~qU4h{1tb|^Kfu}J#p^E))_A-n7cH7(L!W|usOodeLq2pC#!VWWOV^>32SdRey_ z+YD{vJ7D6vbNk1pL0M1YTE#ydn)F`nJp@maFz<*6bD8-o>ax?_y}<4-+v~MF1i4h5 z^p6GG^n}|)BnTkW(bJaoJaz5pMth#G0;r$RsBj7im(I7(%gzfpTsjN8d{MJT-+w}R znEvp0m|)*(k=tRAK%4u0Hb6Mk7h7uz-Vac^z2LuMsloe?}?=AdMcIijgQ zt>4TyusDpKR7Dx>*yWLy^oy}&DM3S^&%c&RiB{$IT8Ci~hx?Cik+l=mhFTbJSZ$ZQ(10zRK9PM&U&gYW>-r zKSG;}%}xc&;|H5ARg>Wr_2TL1uP#sSan|c#@je)JrMJ7BvReQTN9uQ8WG6Dd=U7Cv zG@ROA<*yj3(R@z4OiC^%LIvo2H&vSlDFFv40CJOY{rTj3d4fU#^bPcF zu<^HCsReuBP4lCUWAJ73pCC7qSJK+2=`dxS5veEr3VIp9C#W{y%j5-|;yA<4pk$rTFJ_ zm-g?G>|!og&U7rtoael1+N6ZCRU1mbqiT$ffTjlqrZH-x#`b`VflXGk3i&?+ASo=1in$o0Q zx_+23JC}G-E5P$9kneWB3Fw-$-7(boY+AMRs_ltgPRkUfzKo?x;GP)vo2*Z-Ri%g5 zG@xPrDk`$11>I#Co`9`WKLGlmGu8l5mPQCEPt$Wh_ne1qe%NIm#$~RdhY$QK_t}e{ zq7)-l<%e&JsE|k^=x}ha3_0C8gJ_6pboPFrxmGiU2xwjk%q%&>EPX^pz(bVTNH@VKTJf7ab-E8N(cRNni1< z6sI+X$gHS&jeBPfjI}bt_R)KHKTmF&{Dx3OLt54#vw;xhd*B?4?pK^^ano#?S#X^y z6gHFmBBaTq=BjAml~Wz|M+voKCRa(P3OonL!w#b*Y^IR1Xsrm@k|D)M7$)OhbNqP1 z-q)ac>H%tE=GL;3cXO{i$m*9F+P$~w+o~UW@#uvsh&q+Wk75`ZB?)(ln|8)Bpp#1G#8;-3gMf!hoz`F2VyTVu+d z>>YGt$EA41nbCftgFyXS|MPY^Melmw9{s7_Z<rV zTTnBr$7w<~Fzzb#T<^_4t@XyW5dJV2dy{Y>hs|P9scB_okEH)q7s@7y&UlFJLb;9& zRws3}dA9B=pt;b9uM&`N)@)(r&WOG*T22z=_cSAyA4!s?y^`2B$>g%gZ+ho(j;ggI zCO2b2URO@~ks?^bq%WE9&>Wu5`^?ZVo#{)+R{Hlj!>98s7_fu))am(QIsJ_O8DapG zullqwfR}VVzpQ3IxU7Z^^ZmQOK;+HPbj3E9biHc>* z5-#qgQjRYBMk#^qvF(lOoAg}#;jd)bw@9%X$Zb(e7&D-{(myqZ2c~rlMCOz9Z!!tr zwX^l*U$w84mo_p5+Qe;fHDbM(N^DDwu=VSusbyp;uj-s*laRi@;H&v8zoXatdkkz@ zHyUM7vF`%%!|bYAN8}8o6wf|g6-ss6L7NJ6rzD5cb)}^ORR_s`CXm&x0tZ2&jNvq!o8vb#yFAAa2Q}WReA?LU);b@@3$stRB&K z*UT2-a<{taRaH%H*L;ud8I+UTwjOezxWh-BrN?oGYaO1OF;1AfY$)MB0nO<2*lhkX zaU-{$sSVhU+EQ~7!S1=yE*wFcPE%DYw>YQ4va|swh@HWgB!F`xc-AE8VfX()bLxLl z{6E=yt_Sb9!3BlBCS&TdBR|ZA5F(VjPfz1+Qqe$+B#&pTj#;WS%IqD!E=`brl0r=o zTpzPs6O_-SOlxl49=t7E7jD#?q|d%S3n9D#n&SfH(S*tHq1tw0{Z9oE#OI1|g8ab~ zC1aWfzn39h6V@5bPs*is8?qXs1g%aVE8+4^o92?hG5F&n4#%9NA&>GrSX!RG)JXFPZc9 z$)8*HSf`UUjc9s=&x4Y1wL=15yV7kgR3~$?@i>cW)iTV|S~8Cjv2)nLBLATkWHnPz2Vs~ zE4T%>?%D-a8?I+M#g3G~GZF26Ov6;{dq7oJ6_BBJn_t2%@nPh$!$FW#=#4Hnb68YF?wHKP{07GG0=Z}Ij>hQ%}rcXx# z2U!SqJ>l5{<;@?uw?cWz7AxQKV;IV`d3@RiX{wR4InL z*y-1_0cb+ER)sTH!%iHL(_Qe{G)pPlrHSFhv;8-G{&P+~k}H2+{IZd$Az3)0FwVpf zZHV-ZcYH)9@)=h|1s&RKLMP8T3{9QN+Pxm)<4#o!u<6(_f46dco7+RWrv@U?R%2WF zd6w8y93(JQp0yj7i_}~B-UOft+gcerk35P2UEkgH`MV^EfC*` z7h!nC^^b-Ae>UE17$i4M6VBo0k(rzMOwZHm=3NDBSh*OMpV54q^t`94V$VCCb`M6)vgBNYHY%q|^puo!!mZYzj zbr1noZRg}He&IhXwl1pgVy8{BVI+6?a)0JYL}nQFZl{KRPQ8cby2C3ZppV*(N>M8x zHSUO!FCG&AGUr@jWfKH3^(d8fuXN@xOQ@NBpiTKBL`L{|xTmt4-vHlL<0RxN;ZUVd zvSXcD`EbmgtuNhYQgGay#v@6x^mTNMgbs19x-lYo=;IxuGt2#|AiW>Zt<Nbz=2|Wx_Y@V zUZga03yh$(nj>-g{>GK{iQ{K%P>gW@=3z>{a~_X|R`kzE)_e6767S07SjFo3QKkmB zf|9+0uerg!X4R}h^KUznhu|%s{%E&~m3Ruo&Xk3vAKg?0KRUAeVnVY_uP($m@ymK} z(pMj^^_aAjI@1WsAO4b7cg(XAJ}vD5TeN;qATx@ngRV2JeiY3revR8 zk@1#1idx)c;%di6Qb@8!pWL3HG*Z!2@txVl51I!!`ynzl&AzAZ1Tll%yAME~5au5U^; ztIXv;E(&m*kqti(Yi#9nM8K=#h4I zQ~Z%v@F!3GDL8D}@eh3QmHJO9{^V2a-6zVRnk@{D5XRk`M}w!eQbVq%3l`QPGQ9;Vr6?hkJB3=&wRDtabumV ze%xzxgJue@ZjTN5Gdf(R8@!#0&o;EyN~Ko?bAV`J5~01Df>N6=T34-izF*YaX|C9b z9trqV(Yqg`bTnn^zo&!k74-B+ZG`_KrJA%ezFFf1LO$3P&;Eg>BLPn zG3Ie$WT_XtbdSwni`o-C)8~9TSl%9<@urqkPUr!LYeB#p`Ur)Q#IV}6LXbVNij#(| z6fMz6?}pa1cR@;fQ+cwHx)>Oq-*qBkbs$Hdo2Mz~}BMKtsirUz_h+Tr6+29Nx z83ta70O5Ejx*_rj_@%>l|!ITqd6L_SC2WY%IUHvP({jscZ z?FZ)e5CEtS=yeCJUGK>=+Eg^3D!cEwPIIq$`9S)?D%D222=hY5L2d7!uY2KV%2Eo9 z3mGDy{**@{UHhi!VXBwrjk}g4aBFhL2F?&89Xn-8#02@vcnZh>J zhC7_2$5|X%w0^0d}G6Ly@GQ&##D0(`H4!yy6VHk9V>r z!8}X|CV`7Rksu5O1=zJh8pg>^lbHv|A}JsQuyh1{Bp(1Q0O5yJQsQU;NLe&BDZHiq z9yNpj*iHBc6}>r*7YfnV{#lK#eS2>`_>C4 zC$nE6-);$;#VPF4b&o^5d>*AHquOX7-htk@R)BEQK3(@Rgl_iFV<~(zv?&kbB3E_| z0{UL%>4d&z9<}=$836P*9suLQ4G`I1Bm5NskZ2);1lIT{($H{h+-qhVS=mKZ>7C|o_<$Tx;z=&v^i0|Wz{+`3-+EPf!tRn z14A~!V;$M{wc1!Z4K#4lUZhu>Im5=I@>{Ne;BC7{0%Ei8o`}x?K);6r%a1`Vj+5`( zcQ{32Xns{i3;R4D$|YP-x{gDutcHe2UT!~7F>8L(f%{JPP+Ok7T0rAD1WELqC{&Q_ zj#=4Yr;uN17FDEV1dV^3ea%tJi^8nd4|*s!kMx7R{`l}wky6${SC6?A#FzbMiaL-y3=Bhf7N~~%zyNI(vPRHwvhdS z{#tAbd9JgL9d_V85Xy!2ofBXn)hQc!mt3I1)}Th1NeikrX#8$5Kc0TSCdpdottyc? z0qA!XJNVFVzG)&O)3LSeHgc?U)srSBlhnxW#UM*|#DWU8iup>rkV=H9SLDr35E)6d;YEzH_ywMD&Py(iPEYr{W;37anC{=XU)d@EX%#0zYB3H2=$K;&X6^E9t!T~S}z$6?m6IH34K>-8n9?vYN8!;Vqf zPQ>w-?Li=_vF@gu0bOEFv2r$Rxq1^DM5Se_wzRa?ZvNvVx0}7gE(u#g0{}+EEfhP= z)xO2Ob1j_dc>BVW)e=F%Yy|fL<@aZLNEpXrmi>c`w^LMmwgSn?`7#@Cr3TB`Y6o)F z>uXbgipJ8U-KaIwnh1$`d0O{`bMsY;m#(FL+4kyJvsYF>IzwWdQCkj(Pr8p5Un845 zIk+hAu7`oKq{^KiOGtHieDtu7){#t&^gB*o7kB66B6yOIPGa(~mAg|^ko(*1F&PqH zY0?=d8UhPbml?J7B^tR=>pErq%&B?ws_APkyV*}N6rv_X&zbah%*VW0Z6~?JOGcY{@*0z?Rw4LD#^zd2aeU(il_3Lz55#F$oblz^XuZK;6E3k&O_X>>{ z<6I&OZ}LWZ0T_~u?V)Pt(eqOf)cx2R#+3oH*?8+^tN*-$2AQYWmYXiLbI`qXk`rGK z#YN2p#=dQ6jo-*4zhZhhmE)-$>|5y15CkED(=sgVo2yc7S*QpZhd)9PAl!5+UY|KE zHt~i>l93p7-t!SAVPDXDG@p$6m^60#jqO2t50AvWY!900lk-;~Uoo@Oyq-)W{|rB; ziL-Am0q96~-u0!TF}Gx`nvcWJse!#@_Yc1us=t=8l>AK8*58mSHGH~oSCpw4aQdr6 zO-?q7JApe}%llPdlTk$Ia+4eJ#50I<&aAqQ`&uE8@+2l)<-+(RpJYMFfZ6GSBK0Bj zX@zj>YAH{`>FL#@)n#sx-B_AZrLah0Z=b=n-(1EUHYaInIn-gNGO2p#^2}*XQckv7 ze}*zL=R_{~_!TaVNXKSwn>%jv(UxcNCnuQr#urrXB1u|w>`aG2Wi zrhO52=Eyw|NbFf>6Yt@|SG6F^QjeoT+G^kTB2I=jTRxwM=Ovok>W=3JZouOep_$2t zX@*nIb5MvnW4GLV=g4QKWl01vQ#_LRWR#l3ei9LEeUov{5HjAg>*9^WItyY2OMXoc|m<= zuoHf4<&4hLf> zP{c^(Ed8cYh}0QOTi$~yctNA?1{}aBOa@6u;I|Z8|3)I#;xi-Nx+9_gZ@Sd*-y4C1 zq{R;YpC`1Q_#Yf57wymMO^)5}O}V|oI=E&6$=4x)$NvnzfFh8u%qm@eIP7J59idPlT$y&@n}PtdC2ANshS)*{UuQ<4GCc13=*o z9bycBskrSOoYsN>4nPt?F0NEQeZ@L{2?K!0j2{A{*i8rt;IwrM#}^%o;d1^4tVHo)6bvdnUfgs zWPbpSqH)%J5^wjb@>K}072fjzEl_0n>wjbsF(F&%?LjeOum$1=GC?PuzTZs;SPsz#B1Q~gmkGlU>^rUQ~O{_XPjP{6Bm1$Y2N z5sn~6-;gJl09pi&!_qo-Izb$@qiYbmJ2H&7urs%g27dbPU{>$%S@~976BGQ;3j){E ZRKqLNHN)glEq|L{KG%2#Q+glt{{U`}nFIg; literal 24947 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 - Health status of closed issues [can't be edited](https://gitlab.com/gitlab-org/gitlab/-/issues/220867) in GitLab 13.4 and later. > - Issue health status visible in issue lists [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45141) in GitLab 13.6. > - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/213567) in GitLab 13.7. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218618) in GitLab 15.4: health status is visible on issue cards in issue boards. To help you track issue statuses, you can assign a status to each issue. This status marks issues as progressing as planned or needing attention to keep on schedule. @@ -704,7 +705,11 @@ To edit health status of an issue: - Needs attention (amber) - At risk (red) -You can then see the issue's status in the issues list and the epic tree. +You can see the issue’s health status in: + +- Issues list +- Epic tree +- Issue cards in issue boards After an issue is closed, its health status can't be edited and the **Edit** button becomes disabled until the issue is reopened. diff --git a/lib/gitlab/ci/pipeline/chain/assign_partition.rb b/lib/gitlab/ci/pipeline/chain/assign_partition.rb index f946041db31..4b8efe13d44 100644 --- a/lib/gitlab/ci/pipeline/chain/assign_partition.rb +++ b/lib/gitlab/ci/pipeline/chain/assign_partition.rb @@ -17,9 +17,12 @@ module Gitlab private - # TODO handle parent-child pipelines def find_partition_id - ::Ci::Pipeline.current_partition_value + if @command.creates_child_pipeline? + @command.parent_pipeline_partition_id + else + ::Ci::Pipeline.current_partition_value + end end end end diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb index 2419b039f24..14c320f77bf 100644 --- a/lib/gitlab/ci/pipeline/chain/command.rb +++ b/lib/gitlab/ci/pipeline/chain/command.rb @@ -80,6 +80,10 @@ module Gitlab bridge&.parent_pipeline end + def parent_pipeline_partition_id + parent_pipeline.partition_id if creates_child_pipeline? + end + def creates_child_pipeline? bridge&.triggers_child_pipeline? end diff --git a/lib/gitlab/database/gitlab_schemas.yml b/lib/gitlab/database/gitlab_schemas.yml index 7d1addd1a3d..6d290a14896 100644 --- a/lib/gitlab/database/gitlab_schemas.yml +++ b/lib/gitlab/database/gitlab_schemas.yml @@ -403,6 +403,7 @@ plans: :gitlab_main pool_repositories: :gitlab_main postgres_async_indexes: :gitlab_shared postgres_autovacuum_activity: :gitlab_shared +postgres_constraints: :gitlab_shared postgres_foreign_keys: :gitlab_shared postgres_index_bloat_estimates: :gitlab_shared postgres_indexes: :gitlab_shared diff --git a/lib/gitlab/database/partitioning/convert_table_to_first_list_partition.rb b/lib/gitlab/database/partitioning/convert_table_to_first_list_partition.rb new file mode 100644 index 00000000000..f45cf02ec9b --- /dev/null +++ b/lib/gitlab/database/partitioning/convert_table_to_first_list_partition.rb @@ -0,0 +1,214 @@ +# frozen_string_literal: true + +module Gitlab + module Database + module Partitioning + class ConvertTableToFirstListPartition + UnableToPartition = Class.new(StandardError) + + include Gitlab::Database::MigrationHelpers + + SQL_STATEMENT_SEPARATOR = ";\n\n" + + attr_reader :partitioning_column, :table_name, :parent_table_name, :zero_partition_value + + def initialize(migration_context:, table_name:, parent_table_name:, partitioning_column:, zero_partition_value:) + @migration_context = migration_context + @connection = migration_context.connection + @table_name = table_name + @parent_table_name = parent_table_name + @partitioning_column = partitioning_column + @zero_partition_value = zero_partition_value + end + + def prepare_for_partitioning + assert_existing_constraints_partitionable + + add_partitioning_check_constraint + end + + def revert_preparation_for_partitioning + migration_context.remove_check_constraint(table_name, partitioning_constraint.name) + end + + def partition + assert_existing_constraints_partitionable + assert_partitioning_constraint_present + create_parent_table + attach_foreign_keys_to_parent + + migration_context.with_lock_retries(raise_on_exhaustion: true) do + migration_context.execute(sql_to_convert_table) + end + end + + def revert_partitioning + migration_context.with_lock_retries(raise_on_exhaustion: true) do + migration_context.execute(<<~SQL) + ALTER TABLE #{connection.quote_table_name(parent_table_name)} + DETACH PARTITION #{connection.quote_table_name(table_name)}; + SQL + + alter_sequences_sql = alter_sequence_statements(old_table: parent_table_name, new_table: table_name) + .join(SQL_STATEMENT_SEPARATOR) + + migration_context.execute(alter_sequences_sql) + + # This takes locks for all the foreign keys that the parent table had. + # However, those same locks were taken while detaching the partition, and we can't avoid that. + # If we dropped the foreign key before detaching the partition to avoid this locking, + # the drop would cascade to the child partitions and drop their foreign keys as well + migration_context.drop_table(parent_table_name) + end + + add_partitioning_check_constraint + end + + private + + attr_reader :connection, :migration_context + + delegate :quote_table_name, :quote_column_name, to: :connection + + def sql_to_convert_table + # The critical statement here is the attach_table_to_parent statement. + # The following statements could be run in a later transaction, + # but they acquire the same locks so it's much faster to incude them + # here. + [ + attach_table_to_parent_statement, + alter_sequence_statements(old_table: table_name, new_table: parent_table_name), + remove_constraint_statement + ].flatten.join(SQL_STATEMENT_SEPARATOR) + end + + def table_identifier + "#{connection.current_schema}.#{table_name}" + end + + def assert_existing_constraints_partitionable + violating_constraints = Gitlab::Database::PostgresConstraint + .by_table_identifier(table_identifier) + .primary_or_unique_constraints + .not_including_column(partitioning_column) + .to_a + + return if violating_constraints.empty? + + violation_messages = violating_constraints.map { |c| "#{c.name} on (#{c.column_names.join(', ')})" } + + raise UnableToPartition, <<~MSG + Constraints on #{table_name} are incompatible with partitioning on #{partitioning_column} + + All primary key and unique constraints must include the partitioning column. + Violations: + #{violation_messages.join("\n")} + MSG + end + + def partitioning_constraint + constraints_on_column = Gitlab::Database::PostgresConstraint + .by_table_identifier(table_identifier) + .check_constraints + .valid + .including_column(partitioning_column) + + constraints_on_column.to_a.find do |constraint| + constraint.definition == "CHECK ((#{partitioning_column} = #{zero_partition_value}))" + end + end + + def assert_partitioning_constraint_present + return if partitioning_constraint + + raise UnableToPartition, <<~MSG + Table #{table_name} is not ready for partitioning. + Before partitioning, a check constraint must enforce that (#{partitioning_column} = #{zero_partition_value}) + MSG + end + + def add_partitioning_check_constraint + return if partitioning_constraint.present? + + check_body = "#{partitioning_column} = #{connection.quote(zero_partition_value)}" + # Any constraint name would work. The constraint is found based on its definition before partitioning + migration_context.add_check_constraint(table_name, check_body, 'partitioning_constraint') + + raise UnableToPartition, 'Error adding partitioning constraint' unless partitioning_constraint.present? + end + + def create_parent_table + migration_context.execute(<<~SQL) + CREATE TABLE IF NOT EXISTS #{quote_table_name(parent_table_name)} ( + LIKE #{quote_table_name(table_name)} INCLUDING ALL + ) PARTITION BY LIST(#{quote_column_name(partitioning_column)}) + SQL + end + + def attach_foreign_keys_to_parent + migration_context.foreign_keys(table_name).each do |fk| + # At this point no other connection knows about the parent table. + # Thus the only contended lock in the following transaction is on fk.to_table. + # So a deadlock is impossible. + + # If we're rerunning this migration after a failure to acquire a lock, the foreign key might already exist. + # Don't try to recreate it in that case + if migration_context.foreign_keys(parent_table_name) + .any? { |p_fk| p_fk.options[:name] == fk.options[:name] } + next + end + + migration_context.with_lock_retries(raise_on_exhaustion: true) do + migration_context.add_foreign_key(parent_table_name, fk.to_table, **fk.options) + end + end + end + + def attach_table_to_parent_statement + <<~SQL + ALTER TABLE #{quote_table_name(parent_table_name)} + ATTACH PARTITION #{table_name} + FOR VALUES IN (#{zero_partition_value}) + SQL + end + + def alter_sequence_statements(old_table:, new_table:) + sequences_owned_by(old_table).map do |seq_info| + seq_name, column_name = seq_info.values_at(:name, :column_name) + <<~SQL.chomp + ALTER SEQUENCE #{quote_table_name(seq_name)} OWNED BY #{quote_table_name(new_table)}.#{quote_column_name(column_name)} + SQL + end + end + + def remove_constraint_statement + <<~SQL + ALTER TABLE #{quote_table_name(parent_table_name)} + DROP CONSTRAINT #{quote_table_name(partitioning_constraint.name)} + SQL + end + + # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/373887 + def sequences_owned_by(table_name) + sequence_data = connection.exec_query(<<~SQL, nil, [table_name]) + SELECT seq_pg_class.relname AS seq_name, + dep_pg_class.relname AS table_name, + pg_attribute.attname AS col_name + FROM pg_class seq_pg_class + INNER JOIN pg_depend ON seq_pg_class.oid = pg_depend.objid + INNER JOIN pg_class dep_pg_class ON pg_depend.refobjid = dep_pg_class.oid + INNER JOIN pg_attribute ON dep_pg_class.oid = pg_attribute.attrelid + AND pg_depend.refobjsubid = pg_attribute.attnum + WHERE seq_pg_class.relkind = 'S' + AND dep_pg_class.relname = $1 + SQL + + sequence_data.map do |seq_info| + name, column_name = seq_info.values_at('seq_name', 'col_name') + { name: name, column_name: column_name } + end + end + end + end + end +end diff --git a/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb b/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb index a541ecf5316..695a5d7ec77 100644 --- a/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb +++ b/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb @@ -251,6 +251,54 @@ module Gitlab create_sync_trigger(source_table_name, trigger_name, function_name) end + def prepare_constraint_for_list_partitioning(table_name:, partitioning_column:, parent_table_name:, initial_partitioning_value:) + validate_not_in_transaction!(:prepare_constraint_for_list_partitioning) + + Gitlab::Database::Partitioning::ConvertTableToFirstListPartition + .new(migration_context: self, + table_name: table_name, + parent_table_name: parent_table_name, + partitioning_column: partitioning_column, + zero_partition_value: initial_partitioning_value + ).prepare_for_partitioning + end + + def revert_preparing_constraint_for_list_partitioning(table_name:, partitioning_column:, parent_table_name:, initial_partitioning_value:) + validate_not_in_transaction!(:revert_preparing_constraint_for_list_partitioning) + + Gitlab::Database::Partitioning::ConvertTableToFirstListPartition + .new(migration_context: self, + table_name: table_name, + parent_table_name: parent_table_name, + partitioning_column: partitioning_column, + zero_partition_value: initial_partitioning_value + ).revert_preparation_for_partitioning + end + + def convert_table_to_first_list_partition(table_name:, partitioning_column:, parent_table_name:, initial_partitioning_value:) + validate_not_in_transaction!(:convert_table_to_first_list_partition) + + Gitlab::Database::Partitioning::ConvertTableToFirstListPartition + .new(migration_context: self, + table_name: table_name, + parent_table_name: parent_table_name, + partitioning_column: partitioning_column, + zero_partition_value: initial_partitioning_value + ).partition + end + + def revert_converting_table_to_first_list_partition(table_name:, partitioning_column:, parent_table_name:, initial_partitioning_value:) + validate_not_in_transaction!(:revert_converting_table_to_first_list_partition) + + Gitlab::Database::Partitioning::ConvertTableToFirstListPartition + .new(migration_context: self, + table_name: table_name, + parent_table_name: parent_table_name, + partitioning_column: partitioning_column, + zero_partition_value: initial_partitioning_value + ).revert_partitioning + end + private def assert_table_is_allowed(table_name) diff --git a/lib/gitlab/database/postgres_constraint.rb b/lib/gitlab/database/postgres_constraint.rb new file mode 100644 index 00000000000..fa590914332 --- /dev/null +++ b/lib/gitlab/database/postgres_constraint.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Gitlab + module Database + # Backed by the postgres_constraints view + class PostgresConstraint < SharedModel + IDENTIFIER_REGEX = /^\w+\.\w+$/.freeze + self.primary_key = :oid + + scope :check_constraints, -> { where(constraint_type: 'c') } + scope :primary_key_constraints, -> { where(constraint_type: 'p') } + scope :unique_constraints, -> { where(constraint_type: 'u') } + scope :primary_or_unique_constraints, -> { where(constraint_type: %w[u p]) } + + scope :including_column, ->(column) { where("? = ANY(column_names)", column) } + scope :not_including_column, ->(column) { where.not("? = ANY(column_names)", column) } + + scope :valid, -> { where(constraint_valid: true) } + + scope :by_table_identifier, ->(identifier) do + unless identifier =~ IDENTIFIER_REGEX + raise ArgumentError, "Table name is not fully qualified with a schema: #{identifier}" + end + + where(table_identifier: identifier) + end + end + end +end diff --git a/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512.rb b/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512.rb deleted file mode 100644 index 4bfb5f9e64c..00000000000 --- a/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module DoorkeeperSecretStoring - class Pbkdf2Sha512 < ::Doorkeeper::SecretStoring::Base - STRETCHES = 20_000 - # An empty salt is used because we need to look tokens up solely by - # their hashed value. Additionally, tokens are always cryptographically - # pseudo-random and unique, therefore salting provides no - # additional security. - SALT = '' - - def self.transform_secret(plain_secret) - return plain_secret unless Feature.enabled?(:hash_oauth_tokens) - - Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512.digest(plain_secret, STRETCHES, SALT) - end - - ## - # Determines whether this strategy supports restoring - # secrets from the database. This allows detecting users - # trying to use a non-restorable strategy with +reuse_access_tokens+. - def self.allows_restoring_secrets? - false - end - end - end -end diff --git a/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512.rb b/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512.rb new file mode 100644 index 00000000000..e0884557496 --- /dev/null +++ b/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true +module Gitlab + module DoorkeeperSecretStoring + module Secret + class Pbkdf2Sha512 < ::Doorkeeper::SecretStoring::Base + STRETCHES = 20_000 + # An empty salt is used because we need to look tokens up solely by + # their hashed value. Additionally, tokens are always cryptographically + # pseudo-random and unique, therefore salting provides no + # additional security. + SALT = '' + + def self.transform_secret(plain_secret, stored_as_hash = false) + return plain_secret if Feature.disabled?(:hash_oauth_secrets) && !stored_as_hash + + Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512.digest(plain_secret, STRETCHES, SALT) + end + + ## + # Determines whether this strategy supports restoring + # secrets from the database. This allows detecting users + # trying to use a non-restorable strategy with +reuse_access_tokens+. + def self.allows_restoring_secrets? + false + end + + ## + # Securely compare the given +input+ value with a +stored+ value + # processed by +transform_secret+. + def self.secret_matches?(input, stored) + stored_as_hash = stored.starts_with?('$pbkdf2-') + transformed_input = transform_secret(input, stored_as_hash) + ActiveSupport::SecurityUtils.secure_compare transformed_input, stored + end + end + end + end +end diff --git a/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512.rb b/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512.rb new file mode 100644 index 00000000000..f9e6d4076f3 --- /dev/null +++ b/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Gitlab + module DoorkeeperSecretStoring + module Token + class Pbkdf2Sha512 < ::Doorkeeper::SecretStoring::Base + STRETCHES = 20_000 + # An empty salt is used because we need to look tokens up solely by + # their hashed value. Additionally, tokens are always cryptographically + # pseudo-random and unique, therefore salting provides no + # additional security. + SALT = '' + + def self.transform_secret(plain_secret) + return plain_secret unless Feature.enabled?(:hash_oauth_tokens) + + Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512.digest(plain_secret, STRETCHES, SALT) + end + + ## + # Determines whether this strategy supports restoring + # secrets from the database. This allows detecting users + # trying to use a non-restorable strategy with +reuse_access_tokens+. + def self.allows_restoring_secrets? + false + end + end + end + end +end diff --git a/lib/gitlab/github_import/importer/protected_branches_importer.rb b/lib/gitlab/github_import/importer/protected_branches_importer.rb index be3ac3d17c1..b5be823d5ab 100644 --- a/lib/gitlab/github_import/importer/protected_branches_importer.rb +++ b/lib/gitlab/github_import/importer/protected_branches_importer.rb @@ -11,7 +11,7 @@ module Gitlab def each_object_to_import repo = project.import_source - protected_branches = client.branches(repo).select { |branch| branch.protection.enabled } + protected_branches = client.branches(repo).select { |branch| branch.protection&.enabled } protected_branches.each do |protected_branch| object = client.branch_protection(repo, protected_branch.name) next if object.nil? || already_imported?(object) diff --git a/lib/gitlab/import_export/base/relation_factory.rb b/lib/gitlab/import_export/base/relation_factory.rb index 1cbfcbdb595..bbec473d29d 100644 --- a/lib/gitlab/import_export/base/relation_factory.rb +++ b/lib/gitlab/import_export/base/relation_factory.rb @@ -31,6 +31,8 @@ module Gitlab TOKEN_RESET_MODELS = %i[Project Namespace Group Ci::Trigger Ci::Build Ci::Runner ProjectHook ErrorTracking::ProjectErrorTrackingSetting].freeze + attr_reader :relation_name, :importable + def self.create(*args, **kwargs) new(*args, **kwargs).create end diff --git a/lib/gitlab/import_export/base/relation_object_saver.rb b/lib/gitlab/import_export/base/relation_object_saver.rb index ea989487ebd..3c473449ec0 100644 --- a/lib/gitlab/import_export/base/relation_object_saver.rb +++ b/lib/gitlab/import_export/base/relation_object_saver.rb @@ -58,8 +58,19 @@ module Gitlab records.each_slice(BATCH_SIZE) do |batch| valid_records, invalid_records = batch.partition { |record| record.valid? } - invalid_subrelations << invalid_records relation_object.public_send(relation_name) << valid_records + + # Attempt to save some of the invalid subrelations, as they might be valid after all. + # For example, a merge request `Approval` validates presence of merge_request_id. + # It is not present at a time of calling `#valid?` above, since it's indeed missing. + # However, when saving such subrelation against already persisted merge request + # such validation won't fail (e.g. `merge_request.approvals << Approval.new(user_id: 1)`), + # as we're operating on a merge request that has `id` present. + invalid_records.each do |invalid_record| + relation_object.public_send(relation_name) << invalid_record + + invalid_subrelations << invalid_record unless invalid_record.persisted? + end end end end diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml index 270a5c5c258..33e4823f192 100644 --- a/lib/gitlab/import_export/project/import_export.yml +++ b/lib/gitlab/import_export/project/import_export.yml @@ -1020,6 +1020,35 @@ excluded_attributes: - :merge_request_id - :epic_id - :source_merge_request_id + iteration: + - :id + - :title + - :title_html + - :project_id + - :description_html + - :cached_markdown_version + - :iterations_cadence_id + - :sequence + resource_iteration_events: + - :id + - :issue_id + - :merge_request_id + - :iteration_id + iterations_cadence: + - :id + - :last_run_date + - :duration_in_weeks + - :iterations_in_advance + - :automatic + - :group_id + - :created_at + - :updated_at + - :start_date + - :active + - :roll_over + - :description + - :sequence + methods: notes: - :type @@ -1093,6 +1122,11 @@ ee: - epic_issue: - :epic - :issuable_sla + - iteration: + - :iterations_cadence + - resource_iteration_events: + - iteration: + - :iterations_cadence - protected_branches: - :unprotect_access_levels - protected_environments: @@ -1151,12 +1185,22 @@ ee: - :auto_fix_dependency_scanning - :auto_fix_sast project: - - :requirements_enabled - - :requirements_access_level + - :requirements_enabled + - :requirements_access_level resource_iteration_events: - :user_id - :action - :created_at + iteration: + - :iid + - :created_at + - :updated_at + - :start_date + - :due_date + - :group_id + - :description + iterations_cadence: + - :title preloads: issues: diff --git a/lib/gitlab/import_export/project/object_builder.rb b/lib/gitlab/import_export/project/object_builder.rb index bf60d115a25..50a67a746f8 100644 --- a/lib/gitlab/import_export/project/object_builder.rb +++ b/lib/gitlab/import_export/project/object_builder.rb @@ -21,7 +21,7 @@ module Gitlab end def find - return if epic? && group.nil? + return if group_relation_without_group? return find_diff_commit_user if diff_commit_user? return find_diff_commit if diff_commit? @@ -60,7 +60,7 @@ module Gitlab def prepare_attributes attributes.dup.tap do |atts| - atts.delete('group') unless epic? + atts.delete('group') unless epic? || iteration? if label? atts['type'] = 'ProjectLabel' # Always create project labels @@ -141,6 +141,10 @@ module Gitlab klass == MergeRequestDiffCommit end + def iteration? + klass == Iteration + end + # If an existing group milestone used the IID # claim the IID back and set the group milestone to use one available # This is necessary to fix situations like the following: @@ -157,7 +161,13 @@ module Gitlab milestone.ensure_project_iid! milestone.save! end + + def group_relation_without_group? + (epic? || iteration?) && group.nil? + end end end end end + +Gitlab::ImportExport::Project::ObjectBuilder.prepend_mod diff --git a/lib/gitlab/import_export/project/relation_tree_restorer.rb b/lib/gitlab/import_export/project/relation_tree_restorer.rb index 6e9548f393a..47196db6f8a 100644 --- a/lib/gitlab/import_export/project/relation_tree_restorer.rb +++ b/lib/gitlab/import_export/project/relation_tree_restorer.rb @@ -5,7 +5,7 @@ module Gitlab module Project class RelationTreeRestorer < ImportExport::Group::RelationTreeRestorer # Relations which cannot be saved at project level (and have a group assigned) - GROUP_MODELS = [GroupLabel, Milestone, Epic].freeze + GROUP_MODELS = [GroupLabel, Milestone, Epic, Iteration].freeze private diff --git a/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb b/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb index c9449f10cc2..26d963e2407 100644 --- a/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb +++ b/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb @@ -16,6 +16,8 @@ module Gitlab class RedisMetric < BaseMetric include Gitlab::UsageDataCounters::RedisCounter + USAGE_PREFIX = "USAGE_" + def initialize(time_frame:, options: {}) super @@ -31,6 +33,10 @@ module Gitlab options[:prefix] end + def include_usage_prefix? + options.fetch(:include_usage_prefix, true) + end + def value redis_usage_data do total_count(redis_key) @@ -44,7 +50,9 @@ module Gitlab private def redis_key - "USAGE_#{prefix}_#{metric_event}".upcase + key = "#{prefix}_#{metric_event}".upcase + key.prepend(USAGE_PREFIX) if include_usage_prefix? + key end end end diff --git a/lib/gitlab/usage_data_counters.rb b/lib/gitlab/usage_data_counters.rb index 27376b9d231..fa6bde8c19c 100644 --- a/lib/gitlab/usage_data_counters.rb +++ b/lib/gitlab/usage_data_counters.rb @@ -4,7 +4,6 @@ module Gitlab module UsageDataCounters COUNTERS = [ WikiPageCounter, - WebIdeCounter, NoteCounter, SnippetCounter, SearchCounter, @@ -20,7 +19,8 @@ module Gitlab ].freeze COUNTERS_MIGRATED_TO_INSTRUMENTATION_CLASSES = [ - PackageEventCounter + PackageEventCounter, + WebIdeCounter ].freeze UsageDataCounterError = Class.new(StandardError) diff --git a/lib/gitlab/usage_data_counters/base_counter.rb b/lib/gitlab/usage_data_counters/base_counter.rb index 4ab310a2519..5d2ab5eaf74 100644 --- a/lib/gitlab/usage_data_counters/base_counter.rb +++ b/lib/gitlab/usage_data_counters/base_counter.rb @@ -10,7 +10,9 @@ module Gitlab::UsageDataCounters def redis_key(event) require_known_event(event) - "USAGE_#{prefix}_#{event}".upcase + usage_prefix = Gitlab::Usage::Metrics::Instrumentations::RedisMetric::USAGE_PREFIX + + "#{usage_prefix}#{prefix}_#{event}".upcase end def count(event) diff --git a/lib/sidebars/projects/menus/packages_registries_menu.rb b/lib/sidebars/projects/menus/packages_registries_menu.rb index 30110f1a97f..2ddffe42899 100644 --- a/lib/sidebars/projects/menus/packages_registries_menu.rb +++ b/lib/sidebars/projects/menus/packages_registries_menu.rb @@ -66,7 +66,7 @@ module Sidebars end def harbor_registry__menu_item - if Feature.disabled?(:harbor_registry_integration) || context.project.harbor_integration.nil? + if Feature.disabled?(:harbor_registry_integration, context.project) || context.project.harbor_integration.nil? return ::Sidebars::NilMenuItem.new(item_id: :harbor_registry) end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 87ff69fa9eb..9d4acf41ff1 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -39991,6 +39991,9 @@ msgstr "" msgid "The scan has been created." msgstr "" +msgid "The secret is only available when you first create the application." +msgstr "" + msgid "The snippet can be accessed without any authentication." msgstr "" @@ -40726,6 +40729,9 @@ msgstr "" msgid "This is the number of %{billable_users_link_start}billable users%{link_end} on your installation, and this is the minimum number you need to purchase when you renew your license." msgstr "" +msgid "This is the only time the secret is accessible. Copy the secret and store it securely." +msgstr "" + msgid "This is your current session" msgstr "" diff --git a/qa/qa/support/matchers/eventually_matcher.rb b/qa/qa/support/matchers/eventually_matcher.rb index 01d07585f57..3f451f89246 100644 --- a/qa/qa/support/matchers/eventually_matcher.rb +++ b/qa/qa/support/matchers/eventually_matcher.rb @@ -21,6 +21,7 @@ module QA eq be include + match be_truthy be_falsey be_empty diff --git a/spec/controllers/admin/applications_controller_spec.rb b/spec/controllers/admin/applications_controller_spec.rb index 6c423097e70..bf7707f177c 100644 --- a/spec/controllers/admin/applications_controller_spec.rb +++ b/spec/controllers/admin/applications_controller_spec.rb @@ -39,17 +39,43 @@ RSpec.describe Admin::ApplicationsController do end describe 'POST #create' do - it 'creates the application' do - create_params = attributes_for(:application, trusted: true, confidential: false, scopes: ['api']) + context 'with hash_oauth_secrets flag off' do + before do + stub_feature_flags(hash_oauth_secrets: false) + end - expect do - post :create, params: { doorkeeper_application: create_params } - end.to change { Doorkeeper::Application.count }.by(1) + it 'creates the application' do + create_params = attributes_for(:application, trusted: true, confidential: false, scopes: ['api']) - application = Doorkeeper::Application.last + expect do + post :create, params: { doorkeeper_application: create_params } + end.to change { Doorkeeper::Application.count }.by(1) - expect(response).to redirect_to(admin_application_path(application)) - expect(application).to have_attributes(create_params.except(:uid, :owner_type)) + application = Doorkeeper::Application.last + + expect(response).to redirect_to(admin_application_path(application)) + expect(application).to have_attributes(create_params.except(:uid, :owner_type)) + end + end + + context 'with hash_oauth_secrets flag on' do + before do + stub_feature_flags(hash_oauth_secrets: true) + end + + it 'creates the application' do + create_params = attributes_for(:application, trusted: true, confidential: false, scopes: ['api']) + + expect do + post :create, params: { doorkeeper_application: create_params } + end.to change { Doorkeeper::Application.count }.by(1) + + application = Doorkeeper::Application.last + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template :show + expect(application).to have_attributes(create_params.except(:uid, :owner_type)) + end end it 'renders the application form on errors' do @@ -62,17 +88,43 @@ RSpec.describe Admin::ApplicationsController do end context 'when the params are for a confidential application' do - it 'creates a confidential application' do - create_params = attributes_for(:application, confidential: true, scopes: ['read_user']) + context 'with hash_oauth_secrets flag off' do + before do + stub_feature_flags(hash_oauth_secrets: false) + end - expect do - post :create, params: { doorkeeper_application: create_params } - end.to change { Doorkeeper::Application.count }.by(1) + it 'creates a confidential application' do + create_params = attributes_for(:application, confidential: true, scopes: ['read_user']) - application = Doorkeeper::Application.last + expect do + post :create, params: { doorkeeper_application: create_params } + end.to change { Doorkeeper::Application.count }.by(1) - expect(response).to redirect_to(admin_application_path(application)) - expect(application).to have_attributes(create_params.except(:uid, :owner_type)) + application = Doorkeeper::Application.last + + expect(response).to redirect_to(admin_application_path(application)) + expect(application).to have_attributes(create_params.except(:uid, :owner_type)) + end + end + + context 'with hash_oauth_secrets flag on' do + before do + stub_feature_flags(hash_oauth_secrets: true) + end + + it 'creates a confidential application' do + create_params = attributes_for(:application, confidential: true, scopes: ['read_user']) + + expect do + post :create, params: { doorkeeper_application: create_params } + end.to change { Doorkeeper::Application.count }.by(1) + + application = Doorkeeper::Application.last + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template :show + expect(application).to have_attributes(create_params.except(:uid, :owner_type)) + end end end diff --git a/spec/controllers/groups/settings/applications_controller_spec.rb b/spec/controllers/groups/settings/applications_controller_spec.rb index 0804a5536e0..b9457770ed6 100644 --- a/spec/controllers/groups/settings/applications_controller_spec.rb +++ b/spec/controllers/groups/settings/applications_controller_spec.rb @@ -71,17 +71,43 @@ RSpec.describe Groups::Settings::ApplicationsController do group.add_owner(user) end - it 'creates the application' do - create_params = attributes_for(:application, trusted: false, confidential: false, scopes: ['api']) + context 'with hash_oauth_secrets flag on' do + before do + stub_feature_flags(hash_oauth_secrets: true) + end - expect do - post :create, params: { group_id: group, doorkeeper_application: create_params } - end.to change { Doorkeeper::Application.count }.by(1) + it 'creates the application' do + create_params = attributes_for(:application, trusted: false, confidential: false, scopes: ['api']) - application = Doorkeeper::Application.last + expect do + post :create, params: { group_id: group, doorkeeper_application: create_params } + end.to change { Doorkeeper::Application.count }.by(1) - expect(response).to redirect_to(group_settings_application_path(group, application)) - expect(application).to have_attributes(create_params.except(:uid, :owner_type)) + application = Doorkeeper::Application.last + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template :show + expect(application).to have_attributes(create_params.except(:uid, :owner_type)) + end + end + + context 'with hash_oauth_secrets flag off' do + before do + stub_feature_flags(hash_oauth_secrets: false) + end + + it 'creates the application' do + create_params = attributes_for(:application, trusted: false, confidential: false, scopes: ['api']) + + expect do + post :create, params: { group_id: group, doorkeeper_application: create_params } + end.to change { Doorkeeper::Application.count }.by(1) + + application = Doorkeeper::Application.last + + expect(response).to redirect_to(group_settings_application_path(group, application)) + expect(application).to have_attributes(create_params.except(:uid, :owner_type)) + end end it 'renders the application form on errors' do @@ -94,17 +120,43 @@ RSpec.describe Groups::Settings::ApplicationsController do end context 'when the params are for a confidential application' do - it 'creates a confidential application' do - create_params = attributes_for(:application, confidential: true, scopes: ['read_user']) + context 'with hash_oauth_secrets flag off' do + before do + stub_feature_flags(hash_oauth_secrets: false) + end - expect do - post :create, params: { group_id: group, doorkeeper_application: create_params } - end.to change { Doorkeeper::Application.count }.by(1) + it 'creates a confidential application' do + create_params = attributes_for(:application, confidential: true, scopes: ['read_user']) - application = Doorkeeper::Application.last + expect do + post :create, params: { group_id: group, doorkeeper_application: create_params } + end.to change { Doorkeeper::Application.count }.by(1) - expect(response).to redirect_to(group_settings_application_path(group, application)) - expect(application).to have_attributes(create_params.except(:uid, :owner_type)) + application = Doorkeeper::Application.last + + expect(response).to redirect_to(group_settings_application_path(group, application)) + expect(application).to have_attributes(create_params.except(:uid, :owner_type)) + end + end + + context 'with hash_oauth_secrets flag on' do + before do + stub_feature_flags(hash_oauth_secrets: true) + end + + it 'creates a confidential application' do + create_params = attributes_for(:application, confidential: true, scopes: ['read_user']) + + expect do + post :create, params: { group_id: group, doorkeeper_application: create_params } + end.to change { Doorkeeper::Application.count }.by(1) + + application = Doorkeeper::Application.last + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template :show + expect(application).to have_attributes(create_params.except(:uid, :owner_type)) + end end end diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb index 5bf3b4c48bf..9b16dc9a463 100644 --- a/spec/controllers/oauth/applications_controller_spec.rb +++ b/spec/controllers/oauth/applications_controller_spec.rb @@ -113,11 +113,30 @@ RSpec.describe Oauth::ApplicationsController do subject { post :create, params: oauth_params } - it 'creates an application' do - subject + context 'when hash_oauth_tokens flag set' do + before do + stub_feature_flags(hash_oauth_secrets: true) + end - expect(response).to have_gitlab_http_status(:found) - expect(response).to redirect_to(oauth_application_path(Doorkeeper::Application.last)) + it 'creates an application' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to render_template :show + end + end + + context 'when hash_oauth_tokens flag not set' do + before do + stub_feature_flags(hash_oauth_secrets: false) + end + + it 'creates an application' do + subject + + expect(response).to have_gitlab_http_status(:found) + expect(response).to redirect_to(oauth_application_path(Doorkeeper::Application.last)) + end end it 'redirects back to profile page if OAuth applications are disabled' do diff --git a/spec/fixtures/lib/gitlab/import_export/group/project.json b/spec/fixtures/lib/gitlab/import_export/group/project.json index e8e1e53a86a..671ff92087b 100644 --- a/spec/fixtures/lib/gitlab/import_export/group/project.json +++ b/spec/fixtures/lib/gitlab/import_export/group/project.json @@ -205,6 +205,18 @@ "iid": 1, "group_id": 100 }, + "iteration": { + "created_at": "2022-08-15T12:55:42.607Z", + "updated_at": "2022-08-15T12:56:19.269Z", + "start_date": "2022-08-15", + "due_date": "2022-08-21", + "group_id": 260, + "iid": 5, + "description": "iteration description", + "iterations_cadence": { + "title": "iterations cadence" + } + }, "epic_issue": { "id": 78, "relative_position": 1073740323, @@ -239,7 +251,26 @@ "due_date_sourcing_epic_id": null, "milestone_id": null } - } + }, + "resource_iteration_events": [ + { + "user_id": 1, + "created_at": "2022-08-17T13:04:02.495Z", + "action": "add", + "iteration": { + "created_at": "2022-08-15T12:55:42.607Z", + "updated_at": "2022-08-15T12:56:19.269Z", + "start_date": "2022-08-15", + "due_date": "2022-08-21", + "group_id": 260, + "iid": 5, + "description": "iteration description", + "iterations_cadence": { + "title": "iterations cadence" + } + } + } + ] } ], "snippets": [ diff --git a/spec/fixtures/lib/gitlab/import_export/group/tree/project/issues.ndjson b/spec/fixtures/lib/gitlab/import_export/group/tree/project/issues.ndjson index 4759e97228f..9596986dca0 100644 --- a/spec/fixtures/lib/gitlab/import_export/group/tree/project/issues.ndjson +++ b/spec/fixtures/lib/gitlab/import_export/group/tree/project/issues.ndjson @@ -1,3 +1,3 @@ {"id":1,"title":"Fugiat est minima quae maxime non similique.","assignee_id":null,"project_id":8,"author_id":1,"created_at":"2017-07-07T18:13:01.138Z","updated_at":"2017-08-15T18:37:40.807Z","branch_name":null,"description":"Quam totam fuga numquam in eveniet.","state":"opened","iid":1,"updated_by_id":1,"confidential":false,"due_date":null,"moved_to_id":null,"lock_version":null,"time_estimate":0,"closed_at":null,"last_edited_at":null,"last_edited_by_id":null,"group_milestone_id":null,"milestone":{"id":1,"title":"Project milestone","project_id":8,"description":"Project-level milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"group_id":null},"label_links":[{"id":11,"label_id":6,"target_id":1,"target_type":"Issue","created_at":"2017-08-15T18:37:40.795Z","updated_at":"2017-08-15T18:37:40.795Z","label":{"id":6,"title":"group label","color":"#A8D695","project_id":null,"created_at":"2017-08-15T18:37:19.698Z","updated_at":"2017-08-15T18:37:19.698Z","template":false,"description":"","group_id":5,"type":"GroupLabel","priorities":[]}},{"id":11,"label_id":2,"target_id":1,"target_type":"Issue","created_at":"2017-08-15T18:37:40.795Z","updated_at":"2017-08-15T18:37:40.795Z","label":{"id":6,"title":"A project label","color":"#A8D695","project_id":null,"created_at":"2017-08-15T18:37:19.698Z","updated_at":"2017-08-15T18:37:19.698Z","template":false,"description":"","group_id":5,"type":"ProjectLabel","priorities":[]}}]} {"id":2,"title":"Fugiat est minima quae maxime non similique.","assignee_id":null,"project_id":8,"author_id":1,"created_at":"2017-07-07T18:13:01.138Z","updated_at":"2017-08-15T18:37:40.807Z","branch_name":null,"description":"Quam totam fuga numquam in eveniet.","state":"closed","iid":2,"updated_by_id":1,"confidential":false,"due_date":null,"moved_to_id":null,"lock_version":null,"time_estimate":0,"closed_at":null,"last_edited_at":null,"last_edited_by_id":null,"group_milestone_id":null,"milestone":{"id":2,"title":"A group milestone","description":"Group-level milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"group_id":100},"label_links":[{"id":11,"label_id":2,"target_id":1,"target_type":"Issue","created_at":"2017-08-15T18:37:40.795Z","updated_at":"2017-08-15T18:37:40.795Z","label":{"id":2,"title":"A project label","color":"#A8D695","project_id":null,"created_at":"2017-08-15T18:37:19.698Z","updated_at":"2017-08-15T18:37:19.698Z","template":false,"description":"","group_id":5,"type":"ProjectLabel","priorities":[]}}]} -{"id":3,"title":"Issue with Epic","author_id":1,"project_id":8,"created_at":"2019-12-08T19:41:11.233Z","updated_at":"2019-12-08T19:41:53.194Z","position":0,"branch_name":null,"description":"Donec at nulla vitae sem molestie rutrum ut at sem.","state":"opened","iid":3,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"issue_assignees":[],"notes":[],"milestone":{"id":2,"title":"A group milestone","description":"Group-level milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"group_id":100},"epic_issue":{"id":78,"relative_position":1073740323,"epic":{"id":1,"group_id":5,"author_id":1,"assignee_id":null,"iid":1,"updated_by_id":null,"last_edited_by_id":null,"lock_version":0,"start_date":null,"end_date":null,"last_edited_at":null,"created_at":"2019-12-08T19:37:07.098Z","updated_at":"2019-12-08T19:43:11.568Z","title":"An epic","description":null,"start_date_sourcing_milestone_id":null,"due_date_sourcing_milestone_id":null,"start_date_fixed":null,"due_date_fixed":null,"start_date_is_fixed":null,"due_date_is_fixed":null,"closed_by_id":null,"closed_at":null,"parent_id":null,"relative_position":null,"state_id":"opened","start_date_sourcing_epic_id":null,"due_date_sourcing_epic_id":null,"milestone_id":null}}} +{"id":3,"title":"Issue with Epic","author_id":1,"project_id":8,"created_at":"2019-12-08T19:41:11.233Z","updated_at":"2019-12-08T19:41:53.194Z","position":0,"branch_name":null,"description":"Donec at nulla vitae sem molestie rutrum ut at sem.","state":"opened","iid":3,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"issue_assignees":[],"notes":[],"milestone":{"id":2,"title":"A group milestone","description":"Group-level milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"group_id":100},"iteration":{"created_at":"2022-08-15T12:55:42.607Z","updated_at":"2022-08-15T12:56:19.269Z","start_date":"2022-08-15","due_date":"2022-08-21","group_id":260,"iid":5,"description":"iteration description","iterations_cadence":{"title":"iterations cadence"}},"epic_issue":{"id":78,"relative_position":1073740323,"epic":{"id":1,"group_id":5,"author_id":1,"assignee_id":null,"iid":1,"updated_by_id":null,"last_edited_by_id":null,"lock_version":0,"start_date":null,"end_date":null,"last_edited_at":null,"created_at":"2019-12-08T19:37:07.098Z","updated_at":"2019-12-08T19:43:11.568Z","title":"An epic","description":null,"start_date_sourcing_milestone_id":null,"due_date_sourcing_milestone_id":null,"start_date_fixed":null,"due_date_fixed":null,"start_date_is_fixed":null,"due_date_is_fixed":null,"closed_by_id":null,"closed_at":null,"parent_id":null,"relative_position":null,"state_id":"opened","start_date_sourcing_epic_id":null,"due_date_sourcing_epic_id":null,"milestone_id":null}},"resource_iteration_events":[{"user_id":1,"created_at":"2022-08-17T13:04:02.495Z","action":"add","iteration":{"created_at":"2022-08-15T12:55:42.607Z","updated_at":"2022-08-15T12:56:19.269Z","start_date":"2022-08-15","due_date":"2022-08-21","group_id":260,"iid":5,"description":"iteration description","iterations_cadence":{"title":"iterations cadence"}}}]} diff --git a/spec/fixtures/security_reports/deprecated/gl-sast-report.json b/spec/fixtures/security_reports/deprecated/gl-sast-report.json index 2f7e47281e2..c5b0148fe3e 100644 --- a/spec/fixtures/security_reports/deprecated/gl-sast-report.json +++ b/spec/fixtures/security_reports/deprecated/gl-sast-report.json @@ -961,4 +961,4 @@ "url": "https://cwe.mitre.org/data/definitions/120.html", "tool": "flawfinder" } -] +] \ No newline at end of file diff --git a/spec/fixtures/security_reports/feature-branch/gl-sast-report.json b/spec/fixtures/security_reports/feature-branch/gl-sast-report.json index f93233e0ebb..51761583c70 100644 --- a/spec/fixtures/security_reports/feature-branch/gl-sast-report.json +++ b/spec/fixtures/security_reports/feature-branch/gl-sast-report.json @@ -174,4 +174,4 @@ "start_time": "placeholder-value", "end_time": "placeholder-value" } -} +} \ No newline at end of file diff --git a/spec/fixtures/security_reports/feature-branch/gl-secret-detection-report.json b/spec/fixtures/security_reports/feature-branch/gl-secret-detection-report.json index 538364f84a2..4862a504cec 100644 --- a/spec/fixtures/security_reports/feature-branch/gl-secret-detection-report.json +++ b/spec/fixtures/security_reports/feature-branch/gl-secret-detection-report.json @@ -2,4 +2,4 @@ "version": "14.1.2", "vulnerabilities": [], "remediations": [] -} +} \ No newline at end of file diff --git a/spec/fixtures/security_reports/master/gl-common-scanning-report-names.json b/spec/fixtures/security_reports/master/gl-common-scanning-report-names.json index 3cfb3e51ef7..ef2ff7443d3 100644 --- a/spec/fixtures/security_reports/master/gl-common-scanning-report-names.json +++ b/spec/fixtures/security_reports/master/gl-common-scanning-report-names.json @@ -165,4 +165,4 @@ "end_time": "placeholder-value", "status": "success" } -} +} \ No newline at end of file diff --git a/spec/fixtures/security_reports/master/gl-common-scanning-report-without-top-level-scanner.json b/spec/fixtures/security_reports/master/gl-common-scanning-report-without-top-level-scanner.json index 7f092bf5f68..417dc960aff 100644 --- a/spec/fixtures/security_reports/master/gl-common-scanning-report-without-top-level-scanner.json +++ b/spec/fixtures/security_reports/master/gl-common-scanning-report-without-top-level-scanner.json @@ -1,5 +1,6 @@ { - "vulnerabilities": [{ + "vulnerabilities": [ + { "category": "dependency_scanning", "name": "Vulnerability for remediation testing 1", "message": "This vulnerability should have ONE remediation", @@ -12,24 +13,32 @@ "name": "Gemnasium" }, "location": {}, - "identifiers": [{ - "type": "GitLab", - "name": "Foo vulnerability", - "value": "foo" - }], - "links": [{ - "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137" - }], + "identifiers": [ + { + "type": "GitLab", + "name": "Foo vulnerability", + "value": "foo" + } + ], + "links": [ + { + "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137" + } + ], "details": { "commit": { - "name": [{ - "lang": "en", - "value": "The Commit" - }], - "description": [{ - "lang": "en", - "value": "Commit where the vulnerability was identified" - }], + "name": [ + { + "lang": "en", + "value": "The Commit" + } + ], + "description": [ + { + "lang": "en", + "value": "Commit where the vulnerability was identified" + } + ], "type": "commit", "value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19" } @@ -38,4 +47,4 @@ ], "dependency_files": [], "version": "14.0.2" -} +} \ No newline at end of file diff --git a/spec/fixtures/security_reports/master/gl-common-scanning-report.json b/spec/fixtures/security_reports/master/gl-common-scanning-report.json index 2065af15267..1295b44d4df 100644 --- a/spec/fixtures/security_reports/master/gl-common-scanning-report.json +++ b/spec/fixtures/security_reports/master/gl-common-scanning-report.json @@ -1,5 +1,6 @@ { - "vulnerabilities": [{ + "vulnerabilities": [ + { "category": "dependency_scanning", "name": "Vulnerability for remediation testing 1", "message": "This vulnerability should have ONE remediation", @@ -12,24 +13,32 @@ "name": "Gemnasium" }, "location": {}, - "identifiers": [{ - "type": "GitLab", - "name": "Foo vulnerability", - "value": "foo" - }], - "links": [{ - "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137" - }], + "identifiers": [ + { + "type": "GitLab", + "name": "Foo vulnerability", + "value": "foo" + } + ], + "links": [ + { + "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137" + } + ], "details": { "commit": { - "name": [{ - "lang": "en", - "value": "The Commit" - }], - "description": [{ - "lang": "en", - "value": "Commit where the vulnerability was identified" - }], + "name": [ + { + "lang": "en", + "value": "The Commit" + } + ], + "description": [ + { + "lang": "en", + "value": "Commit where the vulnerability was identified" + } + ], "type": "commit", "value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19" } @@ -48,24 +57,32 @@ "name": "Gemnasium" }, "location": {}, - "identifiers": [{ - "type": "GitLab", - "name": "Foo vulnerability", - "value": "foo" - }], - "links": [{ - "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2138" - }], + "identifiers": [ + { + "type": "GitLab", + "name": "Foo vulnerability", + "value": "foo" + } + ], + "links": [ + { + "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2138" + } + ], "details": { "commit": { - "name": [{ - "lang": "en", - "value": "The Commit" - }], - "description": [{ - "lang": "en", - "value": "Commit where the vulnerability was identified" - }], + "name": [ + { + "lang": "en", + "value": "The Commit" + } + ], + "description": [ + { + "lang": "en", + "value": "Commit where the vulnerability was identified" + } + ], "type": "commit", "value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19" } @@ -84,24 +101,32 @@ "name": "Gemnasium" }, "location": {}, - "identifiers": [{ - "type": "GitLab", - "name": "Foo vulnerability", - "value": "foo" - }], - "links": [{ - "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2139" - }], + "identifiers": [ + { + "type": "GitLab", + "name": "Foo vulnerability", + "value": "foo" + } + ], + "links": [ + { + "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2139" + } + ], "details": { "commit": { - "name": [{ - "lang": "en", - "value": "The Commit" - }], - "description": [{ - "lang": "en", - "value": "Commit where the vulnerability was identified" - }], + "name": [ + { + "lang": "en", + "value": "The Commit" + } + ], + "description": [ + { + "lang": "en", + "value": "Commit where the vulnerability was identified" + } + ], "type": "commit", "value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19" } @@ -120,24 +145,32 @@ "name": "Gemnasium" }, "location": {}, - "identifiers": [{ - "type": "GitLab", - "name": "Foo vulnerability", - "value": "foo" - }], - "links": [{ - "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2140" - }], + "identifiers": [ + { + "type": "GitLab", + "name": "Foo vulnerability", + "value": "foo" + } + ], + "links": [ + { + "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2140" + } + ], "details": { "commit": { - "name": [{ - "lang": "en", - "value": "The Commit" - }], - "description": [{ - "lang": "en", - "value": "Commit where the vulnerability was identified" - }], + "name": [ + { + "lang": "en", + "value": "The Commit" + } + ], + "description": [ + { + "lang": "en", + "value": "Commit where the vulnerability was identified" + } + ], "type": "commit", "value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19" } @@ -162,30 +195,37 @@ }, "summary": "The Origin header was changed to an invalid value of http://peachapisecurity.com and the response contained an Access-Control-Allow-Origin header which included this invalid Origin, indicating that the CORS configuration on the server is overly permissive.\n\n\n", "request": { - "headers": [{ - "name": "Host", - "value": "127.0.0.1:7777" - }], + "headers": [ + { + "name": "Host", + "value": "127.0.0.1:7777" + } + ], "method": "GET", "url": "http://127.0.0.1:7777/api/users", "body": "" }, "response": { - "headers": [{ - "name": "Server", - "value": "TwistedWeb/20.3.0" - }], + "headers": [ + { + "name": "Server", + "value": "TwistedWeb/20.3.0" + } + ], "reason_phrase": "OK", "status_code": 200, "body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]" }, - "supporting_messages": [{ + "supporting_messages": [ + { "name": "Origional", "request": { - "headers": [{ - "name": "Host", - "value": "127.0.0.1:7777" - }], + "headers": [ + { + "name": "Host", + "value": "127.0.0.1:7777" + } + ], "method": "GET", "url": "http://127.0.0.1:7777/api/users", "body": "" @@ -194,19 +234,23 @@ { "name": "Recorded", "request": { - "headers": [{ - "name": "Host", - "value": "127.0.0.1:7777" - }], + "headers": [ + { + "name": "Host", + "value": "127.0.0.1:7777" + } + ], "method": "GET", "url": "http://127.0.0.1:7777/api/users", "body": "" }, "response": { - "headers": [{ - "name": "Server", - "value": "TwistedWeb/20.3.0" - }], + "headers": [ + { + "name": "Server", + "value": "TwistedWeb/20.3.0" + } + ], "reason_phrase": "OK", "status_code": 200, "body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]" @@ -215,24 +259,32 @@ ] }, "location": {}, - "identifiers": [{ - "type": "GitLab", - "name": "Foo vulnerability", - "value": "foo" - }], - "links": [{ - "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020" - }], + "identifiers": [ + { + "type": "GitLab", + "name": "Foo vulnerability", + "value": "foo" + } + ], + "links": [ + { + "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020" + } + ], "details": { "commit": { - "name": [{ - "lang": "en", - "value": "The Commit" - }], - "description": [{ - "lang": "en", - "value": "Commit where the vulnerability was identified" - }], + "name": [ + { + "lang": "en", + "value": "The Commit" + } + ], + "description": [ + { + "lang": "en", + "value": "Commit where the vulnerability was identified" + } + ], "type": "commit", "value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19" } @@ -258,30 +310,37 @@ }, "summary": "The Origin header was changed to an invalid value of http://peachapisecurity.com and the response contained an Access-Control-Allow-Origin header which included this invalid Origin, indicating that the CORS configuration on the server is overly permissive.\n\n\n", "request": { - "headers": [{ - "name": "Host", - "value": "127.0.0.1:7777" - }], + "headers": [ + { + "name": "Host", + "value": "127.0.0.1:7777" + } + ], "method": "GET", "url": "http://127.0.0.1:7777/api/users", "body": "" }, "response": { - "headers": [{ - "name": "Server", - "value": "TwistedWeb/20.3.0" - }], + "headers": [ + { + "name": "Server", + "value": "TwistedWeb/20.3.0" + } + ], "reason_phrase": "OK", "status_code": 200, "body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]" }, - "supporting_messages": [{ + "supporting_messages": [ + { "name": "Origional", "request": { - "headers": [{ - "name": "Host", - "value": "127.0.0.1:7777" - }], + "headers": [ + { + "name": "Host", + "value": "127.0.0.1:7777" + } + ], "method": "GET", "url": "http://127.0.0.1:7777/api/users", "body": "" @@ -290,19 +349,23 @@ { "name": "Recorded", "request": { - "headers": [{ - "name": "Host", - "value": "127.0.0.1:7777" - }], + "headers": [ + { + "name": "Host", + "value": "127.0.0.1:7777" + } + ], "method": "GET", "url": "http://127.0.0.1:7777/api/users", "body": "" }, "response": { - "headers": [{ - "name": "Server", - "value": "TwistedWeb/20.3.0" - }], + "headers": [ + { + "name": "Server", + "value": "TwistedWeb/20.3.0" + } + ], "reason_phrase": "OK", "status_code": 200, "body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]" @@ -311,15 +374,19 @@ ] }, "location": {}, - "identifiers": [{ - "type": "GitLab", - "name": "Bar vulnerability", - "value": "bar" - }], - "links": [{ - "name": "CVE-1030", - "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030" - }] + "identifiers": [ + { + "type": "GitLab", + "name": "Bar vulnerability", + "value": "bar" + } + ], + "links": [ + { + "name": "CVE-1030", + "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030" + } + ] }, { "category": "dependency_scanning", @@ -338,57 +405,73 @@ "links": [] } ], - "remediations": [{ - "fixes": [{ - "cve": "CVE-2137" - }], + "remediations": [ + { + "fixes": [ + { + "cve": "CVE-2137" + } + ], "summary": "this remediates CVE-2137", "diff": "dG90YWxseSBsZWdpdCBkaWZm" }, { - "fixes": [{ - "cve": "CVE-2138" - }], + "fixes": [ + { + "cve": "CVE-2138" + } + ], "summary": "this remediates CVE-2138", "diff": "dG90YWxseSBsZWdpdCBkaWZm" }, { - "fixes": [{ - "cve": "CVE-2139" - }, { - "cve": "CVE-2140" - }], + "fixes": [ + { + "cve": "CVE-2139" + }, + { + "cve": "CVE-2140" + } + ], "summary": "this remediates CVE-2139 and CVE-2140", "diff": "dG90YWxseSBsZWdpdGltYXRlIGRpZmYsIDEwLzEwIHdvdWxkIGFwcGx5" }, { - "fixes": [{ - "cve": "CVE-1020" - }], + "fixes": [ + { + "cve": "CVE-1020" + } + ], "summary": "", "diff": "" }, { - "fixes": [{ - "cve": "CVE", - "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3" - }], + "fixes": [ + { + "cve": "CVE", + "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3" + } + ], "summary": "", "diff": "" }, { - "fixes": [{ - "cve": "CVE", - "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3" - }], + "fixes": [ + { + "cve": "CVE", + "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3" + } + ], "summary": "", "diff": "" }, { - "fixes": [{ - "id": "2134", - "cve": "CVE-1" - }], + "fixes": [ + { + "id": "2134", + "cve": "CVE-1" + } + ], "summary": "", "diff": "" } @@ -419,4 +502,4 @@ "status": "success" }, "version": "14.0.2" -} +} \ No newline at end of file diff --git a/spec/fixtures/security_reports/master/gl-sast-missing-scanner.json b/spec/fixtures/security_reports/master/gl-sast-missing-scanner.json index ab3ee348263..fcfd9b831f4 100644 --- a/spec/fixtures/security_reports/master/gl-sast-missing-scanner.json +++ b/spec/fixtures/security_reports/master/gl-sast-missing-scanner.json @@ -799,4 +799,4 @@ "url": "https://cwe.mitre.org/data/definitions/120.html" } ] -} +} \ No newline at end of file diff --git a/spec/fixtures/security_reports/master/gl-sast-report-bandit.json b/spec/fixtures/security_reports/master/gl-sast-report-bandit.json index a80833354ed..d0346479b85 100644 --- a/spec/fixtures/security_reports/master/gl-sast-report-bandit.json +++ b/spec/fixtures/security_reports/master/gl-sast-report-bandit.json @@ -40,4 +40,4 @@ "end_time": "2022-03-11T00:21:50", "status": "success" } -} +} \ No newline at end of file diff --git a/spec/fixtures/security_reports/master/gl-sast-report-gosec.json b/spec/fixtures/security_reports/master/gl-sast-report-gosec.json index 42986ea1045..4c385326c8c 100644 --- a/spec/fixtures/security_reports/master/gl-sast-report-gosec.json +++ b/spec/fixtures/security_reports/master/gl-sast-report-gosec.json @@ -65,4 +65,4 @@ "end_time": "2022-03-15T20:33:17", "status": "success" } -} +} \ No newline at end of file diff --git a/spec/fixtures/security_reports/master/gl-sast-report-minimal.json b/spec/fixtures/security_reports/master/gl-sast-report-minimal.json index 60a67453c9b..5e9273d43b1 100644 --- a/spec/fixtures/security_reports/master/gl-sast-report-minimal.json +++ b/spec/fixtures/security_reports/master/gl-sast-report-minimal.json @@ -65,4 +65,4 @@ "start_time": "placeholder-value", "end_time": "placeholder-value" } -} +} \ No newline at end of file diff --git a/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-bandit.json b/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-bandit.json index 2a60a75366e..037b9fb8d3e 100644 --- a/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-bandit.json +++ b/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-bandit.json @@ -68,4 +68,4 @@ "end_time": "2022-03-11T18:48:22", "status": "success" } -} +} \ No newline at end of file diff --git a/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-gosec.json b/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-gosec.json index 3d8c65d5823..f01d26a69c9 100644 --- a/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-gosec.json +++ b/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-gosec.json @@ -67,4 +67,4 @@ "end_time": "2022-03-15T20:37:05", "status": "success" } -} +} \ No newline at end of file diff --git a/spec/fixtures/security_reports/master/gl-sast-report.json b/spec/fixtures/security_reports/master/gl-sast-report.json index 63504e6fccc..1aa8db1a65f 100644 --- a/spec/fixtures/security_reports/master/gl-sast-report.json +++ b/spec/fixtures/security_reports/master/gl-sast-report.json @@ -197,4 +197,4 @@ "start_time": "placeholder-value", "end_time": "placeholder-value" } -} +} \ No newline at end of file diff --git a/spec/fixtures/security_reports/master/gl-secret-detection-report.json b/spec/fixtures/security_reports/master/gl-secret-detection-report.json index 9b0b2a19beb..21d4f3f1798 100644 --- a/spec/fixtures/security_reports/master/gl-secret-detection-report.json +++ b/spec/fixtures/security_reports/master/gl-secret-detection-report.json @@ -30,4 +30,4 @@ } ], "remediations": [] -} +} \ No newline at end of file diff --git a/spec/frontend/jira_connect/subscriptions/components/sign_in_oauth_button_spec.js b/spec/frontend/jira_connect/subscriptions/components/sign_in_oauth_button_spec.js index 653143a6243..01317eb5dba 100644 --- a/spec/frontend/jira_connect/subscriptions/components/sign_in_oauth_button_spec.js +++ b/spec/frontend/jira_connect/subscriptions/components/sign_in_oauth_button_spec.js @@ -30,7 +30,7 @@ describe('SignInOauthButton', () => { let store; const mockOauthMetadata = { oauth_authorize_url: 'https://gitlab.com/mockOauth', - oauth_token_url: 'https://gitlab.com/mockOauthToken', + oauth_token_path: 'https://gitlab.com/mockOauthToken', oauth_token_payload: { client_id: '543678901', }, @@ -197,7 +197,7 @@ describe('SignInOauthButton', () => { }); it('executes POST request to Oauth token endpoint', () => { - expect(fetchOAuthToken).toHaveBeenCalledWith(mockOauthMetadata.oauth_token_url, { + expect(fetchOAuthToken).toHaveBeenCalledWith(mockOauthMetadata.oauth_token_path, { code: '1234', code_verifier: 'mock-verifier', client_id: mockOauthMetadata.oauth_token_payload.client_id, diff --git a/spec/helpers/jira_connect_helper_spec.rb b/spec/helpers/jira_connect_helper_spec.rb index 4d2fc3d9ee6..97e37023c2d 100644 --- a/spec/helpers/jira_connect_helper_spec.rb +++ b/spec/helpers/jira_connect_helper_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe JiraConnectHelper do describe '#jira_connect_app_data' do + let_it_be(:installation) { create(:jira_connect_installation) } let_it_be(:subscription) { create(:jira_connect_subscription) } let(:user) { create(:user) } @@ -13,11 +14,12 @@ RSpec.describe JiraConnectHelper do stub_application_setting(jira_connect_application_key: client_id) end - subject { helper.jira_connect_app_data([subscription]) } + subject { helper.jira_connect_app_data([subscription], installation) } context 'user is not logged in' do before do allow(view).to receive(:current_user).and_return(nil) + allow(Gitlab).to receive_message_chain('config.gitlab.url') { 'http://test.host' } end it 'includes Jira Connect app attributes' do @@ -36,14 +38,14 @@ RSpec.describe JiraConnectHelper do end context 'with oauth_metadata' do - let(:oauth_metadata) { helper.jira_connect_app_data([subscription])[:oauth_metadata] } + let(:oauth_metadata) { helper.jira_connect_app_data([subscription], installation)[:oauth_metadata] } subject(:parsed_oauth_metadata) { Gitlab::Json.parse(oauth_metadata).deep_symbolize_keys } it 'assigns oauth_metadata' do expect(parsed_oauth_metadata).to include( oauth_authorize_url: start_with('http://test.host/oauth/authorize?'), - oauth_token_url: 'http://test.host/oauth/token', + oauth_token_path: '/oauth/token', state: %r/[a-z0-9.]{32}/, oauth_token_payload: hash_including( grant_type: 'authorization_code', @@ -74,6 +76,30 @@ RSpec.describe JiraConnectHelper do expect(oauth_metadata).to be_nil end end + + context 'with self-managed instance' do + let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'https://gitlab.example.com') } + + it 'points urls to the self-managed instance' do + expect(parsed_oauth_metadata).to include( + oauth_authorize_url: start_with('https://gitlab.example.com/oauth/authorize?'), + oauth_token_path: '/oauth/token' + ) + end + + context 'and jira_connect_oauth_self_managed feature is disabled' do + before do + stub_feature_flags(jira_connect_oauth_self_managed: false) + end + + it 'does not point urls to the self-managed instance' do + expect(parsed_oauth_metadata).not_to include( + oauth_authorize_url: start_with('https://gitlab.example.com/oauth/authorize?'), + oauth_token_path: 'https://gitlab.example.com/oauth/token' + ) + end + end + end end it 'passes group as "skip_groups" param' do diff --git a/spec/lib/bulk_imports/projects/pipelines/merge_requests_pipeline_spec.rb b/spec/lib/bulk_imports/projects/pipelines/merge_requests_pipeline_spec.rb index 3f02356b41e..e780cde4ae2 100644 --- a/spec/lib/bulk_imports/projects/pipelines/merge_requests_pipeline_spec.rb +++ b/spec/lib/bulk_imports/projects/pipelines/merge_requests_pipeline_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe BulkImports::Projects::Pipelines::MergeRequestsPipeline do let_it_be(:user) { create(:user) } + let_it_be(:another_user) { create(:user) } let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project, :repository, group: group) } let_it_be(:bulk_import) { create(:bulk_import, user: user) } @@ -85,6 +86,9 @@ RSpec.describe BulkImports::Projects::Pipelines::MergeRequestsPipeline do describe '#run' do before do group.add_owner(user) + group.add_maintainer(another_user) + + ::BulkImports::UsersMapper.new(context: context).cache_source_user_id(42, another_user.id) allow_next_instance_of(BulkImports::Common::Extractors::NdjsonExtractor) do |extractor| allow(extractor).to receive(:remove_tmp_dir) @@ -293,5 +297,52 @@ RSpec.describe BulkImports::Projects::Pipelines::MergeRequestsPipeline do expect(imported_mr.milestone.title).to eq(attributes.dig('milestone', 'title')) end end + + context 'user assignments' do + let(:attributes) do + { + key => [ + { + 'user_id' => 22, + 'created_at' => '2020-01-07T11:21:21.235Z' + }, + { + 'user_id' => 42, + 'created_at' => '2020-01-08T12:21:21.235Z' + } + ] + } + end + + context 'assignees' do + let(:key) { 'merge_request_assignees' } + + it 'imports mr assignees' do + assignees = imported_mr.merge_request_assignees + + expect(assignees.pluck(:user_id)).to contain_exactly(user.id, another_user.id) + end + end + + context 'approvals' do + let(:key) { 'approvals' } + + it 'imports mr approvals' do + approvals = imported_mr.approvals + + expect(approvals.pluck(:user_id)).to contain_exactly(user.id, another_user.id) + end + end + + context 'reviewers' do + let(:key) { 'merge_request_reviewers' } + + it 'imports mr reviewers' do + reviewers = imported_mr.merge_request_reviewers + + expect(reviewers.pluck(:user_id)).to contain_exactly(user.id, another_user.id) + end + end + end end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb index 7215c65d41b..15df5b2f68c 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb @@ -24,5 +24,24 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::AssignPartition do it 'assigns partition_id to pipeline' do expect { subject }.to change(pipeline, :partition_id).to(current_partition_id) end + + context 'with parent-child pipelines' do + let(:bridge) do + instance_double(Ci::Bridge, + triggers_child_pipeline?: true, + parent_pipeline: instance_double(Ci::Pipeline, partition_id: 125)) + end + + let(:command) do + Gitlab::Ci::Pipeline::Chain::Command.new( + project: project, + current_user: user, + bridge: bridge) + end + + it 'assigns partition_id to pipeline' do + expect { subject }.to change(pipeline, :partition_id).to(125) + end + end end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb index de43e759193..6e8b6e40928 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb @@ -302,13 +302,13 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do context 'when bridge is present' do context 'when bridge triggers a child pipeline' do - let(:bridge) { double(:bridge, triggers_child_pipeline?: true) } + let(:bridge) { instance_double(Ci::Bridge, triggers_child_pipeline?: true) } it { is_expected.to be_truthy } end context 'when bridge triggers a multi-project pipeline' do - let(:bridge) { double(:bridge, triggers_child_pipeline?: false) } + let(:bridge) { instance_double(Ci::Bridge, triggers_child_pipeline?: false) } it { is_expected.to be_falsey } end @@ -321,6 +321,38 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do end end + describe '#parent_pipeline_partition_id' do + let(:command) { described_class.new(bridge: bridge) } + + subject { command.parent_pipeline_partition_id } + + context 'when bridge is present' do + context 'when bridge triggers a child pipeline' do + let(:pipeline) { instance_double(Ci::Pipeline, partition_id: 123) } + + let(:bridge) do + instance_double(Ci::Bridge, + triggers_child_pipeline?: true, + parent_pipeline: pipeline) + end + + it { is_expected.to eq(123) } + end + + context 'when bridge triggers a multi-project pipeline' do + let(:bridge) { instance_double(Ci::Bridge, triggers_child_pipeline?: false) } + + it { is_expected.to be_nil } + end + end + + context 'when bridge is not present' do + let(:bridge) { nil } + + it { is_expected.to be_nil } + end + end + describe '#increment_pipeline_failure_reason_counter' do let(:command) { described_class.new } let(:reason) { :size_limit_exceeded } @@ -345,7 +377,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do describe '#observe_step_duration' do context 'when ci_pipeline_creation_step_duration_tracking is enabled' do it 'adds the duration to the step duration histogram' do - histogram = double(:histogram) + histogram = instance_double(Prometheus::Client::Histogram) duration = 1.hour expect(::Gitlab::Ci::Pipeline::Metrics).to receive(:pipeline_creation_step_duration_histogram) diff --git a/spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb b/spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb new file mode 100644 index 00000000000..af7d751a404 --- /dev/null +++ b/spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb @@ -0,0 +1,246 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Database::Partitioning::ConvertTableToFirstListPartition do + include Gitlab::Database::DynamicModelHelpers + include Database::TableSchemaHelpers + + let(:migration_context) { Gitlab::Database::Migration[2.0].new } + + let(:connection) { migration_context.connection } + let(:table_name) { '_test_table_to_partition' } + let(:table_identifier) { "#{connection.current_schema}.#{table_name}" } + let(:partitioning_column) { :partition_number } + let(:partitioning_default) { 1 } + let(:referenced_table_name) { '_test_referenced_table' } + let(:other_referenced_table_name) { '_test_other_referenced_table' } + let(:parent_table_name) { "#{table_name}_parent" } + + let(:model) { define_batchable_model(table_name, connection: connection) } + + let(:parent_model) { define_batchable_model(parent_table_name, connection: connection) } + + let(:converter) do + described_class.new( + migration_context: migration_context, + table_name: table_name, + partitioning_column: partitioning_column, + parent_table_name: parent_table_name, + zero_partition_value: partitioning_default + ) + end + + before do + # Suppress printing migration progress + allow(migration_context).to receive(:puts) + allow(migration_context.connection).to receive(:transaction_open?).and_return(false) + + connection.execute(<<~SQL) + create table #{referenced_table_name} ( + id bigserial primary key not null + ) + SQL + + connection.execute(<<~SQL) + create table #{other_referenced_table_name} ( + id bigserial primary key not null + ) + SQL + + connection.execute(<<~SQL) + insert into #{referenced_table_name} default values; + insert into #{other_referenced_table_name} default values; + SQL + + connection.execute(<<~SQL) + create table #{table_name} ( + id bigserial not null, + #{partitioning_column} bigint not null default #{partitioning_default}, + referenced_id bigint not null references #{referenced_table_name} (id) on delete cascade, + other_referenced_id bigint not null references #{other_referenced_table_name} (id) on delete set null, + primary key (id, #{partitioning_column}) + ) + SQL + + connection.execute(<<~SQL) + insert into #{table_name} (referenced_id, other_referenced_id) + select #{referenced_table_name}.id, #{other_referenced_table_name}.id + from #{referenced_table_name}, #{other_referenced_table_name}; + SQL + end + + describe "#prepare_for_partitioning" do + subject(:prepare) { converter.prepare_for_partitioning } + + it 'adds a check constraint' do + expect { prepare }.to change { + Gitlab::Database::PostgresConstraint + .check_constraints + .by_table_identifier(table_identifier) + .count + }.from(0).to(1) + end + end + + describe '#revert_prepare_for_partitioning' do + before do + converter.prepare_for_partitioning + end + + subject(:revert_prepare) { converter.revert_preparation_for_partitioning } + + it 'removes a check constraint' do + expect { revert_prepare }.to change { + Gitlab::Database::PostgresConstraint + .check_constraints + .by_table_identifier("#{connection.current_schema}.#{table_name}") + .count + }.from(1).to(0) + end + end + + describe "#convert_to_zero_partition" do + subject(:partition) { converter.partition } + + before do + converter.prepare_for_partitioning + end + + context 'when the primary key is incorrect' do + before do + connection.execute(<<~SQL) + alter table #{table_name} drop constraint #{table_name}_pkey; + alter table #{table_name} add constraint #{table_name}_pkey PRIMARY KEY (id); + SQL + end + + it 'throws a reasonable error message' do + expect { partition }.to raise_error(described_class::UnableToPartition, /#{partitioning_column}/) + end + end + + context 'when there is not a supporting check constraint' do + before do + connection.execute(<<~SQL) + alter table #{table_name} drop constraint partitioning_constraint; + SQL + end + + it 'throws a reasonable error message' do + expect { partition }.to raise_error(described_class::UnableToPartition, /constraint /) + end + end + + it 'migrates the table to a partitioned table' do + fks_before = migration_context.foreign_keys(table_name) + + partition + + expect(Gitlab::Database::PostgresPartition.for_parent_table(parent_table_name).count).to eq(1) + expect(migration_context.foreign_keys(parent_table_name).map(&:options)).to match_array(fks_before.map(&:options)) + + connection.execute(<<~SQL) + insert into #{table_name} (referenced_id, other_referenced_id) select #{referenced_table_name}.id, #{other_referenced_table_name}.id from #{referenced_table_name}, #{other_referenced_table_name}; + SQL + + # Create a second partition + connection.execute(<<~SQL) + create table #{table_name}2 partition of #{parent_table_name} FOR VALUES IN (2) + SQL + + parent_model.create!(partitioning_column => 2, :referenced_id => 1, :other_referenced_id => 1) + expect(parent_model.pluck(:id)).to match_array([1, 2, 3]) + end + + context 'when an error occurs during the conversion' do + def fail_first_time + # We can't directly use a boolean here, as we need something that will be passed by-reference to the proc + fault_status = { faulted: false } + proc do |m, *args, **kwargs| + next m.call(*args, **kwargs) if fault_status[:faulted] + + fault_status[:faulted] = true + raise 'fault!' + end + end + + def fail_sql_matching(regex) + proc do + allow(migration_context.connection).to receive(:execute).and_call_original + allow(migration_context.connection).to receive(:execute).with(regex).and_wrap_original(&fail_first_time) + end + end + + def fail_adding_fk(from_table, to_table) + proc do + allow(migration_context.connection).to receive(:add_foreign_key).and_call_original + expect(migration_context.connection).to receive(:add_foreign_key).with(from_table, to_table, any_args) + .and_wrap_original(&fail_first_time) + end + end + + where(:case_name, :fault) do + [ + ["creating parent table", lazy { fail_sql_matching(/CREATE/i) }], + ["adding the first foreign key", lazy { fail_adding_fk(parent_table_name, referenced_table_name) }], + ["adding the second foreign key", lazy { fail_adding_fk(parent_table_name, other_referenced_table_name) }], + ["attaching table", lazy { fail_sql_matching(/ATTACH/i) }] + ] + end + + before do + # Set up the fault that we'd like to inject + fault.call + end + + with_them do + it 'recovers from a fault', :aggregate_failures do + expect { converter.partition }.to raise_error(/fault/) + expect(Gitlab::Database::PostgresPartition.for_parent_table(parent_table_name).count).to eq(0) + + expect { converter.partition }.not_to raise_error + expect(Gitlab::Database::PostgresPartition.for_parent_table(parent_table_name).count).to eq(1) + end + end + end + end + + describe '#revert_conversion_to_zero_partition' do + before do + converter.prepare_for_partitioning + converter.partition + end + + subject(:revert_conversion) { converter.revert_partitioning } + + it 'detaches the partition' do + expect { revert_conversion }.to change { + Gitlab::Database::PostgresPartition + .for_parent_table(parent_table_name).count + }.from(1).to(0) + end + + it 'does not drop the child partition' do + expect { revert_conversion }.not_to change { table_oid(table_name) } + end + + it 'removes the parent table' do + expect { revert_conversion }.to change { table_oid(parent_table_name).present? }.from(true).to(false) + end + + it 're-adds the check constraint' do + expect { revert_conversion }.to change { + Gitlab::Database::PostgresConstraint + .check_constraints + .by_table_identifier(table_identifier) + .count + }.by(1) + end + + it 'moves sequences back to the original table' do + expect { revert_conversion }.to change { converter.send(:sequences_owned_by, table_name).count }.from(0) + .and change { converter.send(:sequences_owned_by, parent_table_name).count }.to(0) + end + end +end diff --git a/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb b/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb index 1026b4370a5..8bb9ad2737a 100644 --- a/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb +++ b/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb @@ -41,6 +41,76 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::TableManagementHe allow(migration).to receive(:assert_table_is_allowed) end + context 'list partitioning conversion helpers' do + shared_examples_for 'delegates to ConvertTableToFirstListPartition' do + it 'throws an error if in a transaction' do + allow(migration).to receive(:transaction_open?).and_return(true) + expect { migrate }.to raise_error(/cannot be run inside a transaction/) + end + + it 'delegates to a method on ConvertTableToFirstListPartition' do + expect_next_instance_of(Gitlab::Database::Partitioning::ConvertTableToFirstListPartition, + migration_context: migration, + table_name: source_table, + parent_table_name: partitioned_table, + partitioning_column: partition_column, + zero_partition_value: min_date) do |converter| + expect(converter).to receive(expected_method) + end + + migrate + end + end + + describe '#convert_table_to_first_list_partition' do + it_behaves_like 'delegates to ConvertTableToFirstListPartition' do + let(:expected_method) { :partition } + let(:migrate) do + migration.convert_table_to_first_list_partition(table_name: source_table, + partitioning_column: partition_column, + parent_table_name: partitioned_table, + initial_partitioning_value: min_date) + end + end + end + + describe '#revert_converting_table_to_first_list_partition' do + it_behaves_like 'delegates to ConvertTableToFirstListPartition' do + let(:expected_method) { :revert_partitioning } + let(:migrate) do + migration.revert_converting_table_to_first_list_partition(table_name: source_table, + partitioning_column: partition_column, + parent_table_name: partitioned_table, + initial_partitioning_value: min_date) + end + end + end + + describe '#prepare_constraint_for_list_partitioning' do + it_behaves_like 'delegates to ConvertTableToFirstListPartition' do + let(:expected_method) { :prepare_for_partitioning } + let(:migrate) do + migration.prepare_constraint_for_list_partitioning(table_name: source_table, + partitioning_column: partition_column, + parent_table_name: partitioned_table, + initial_partitioning_value: min_date) + end + end + end + + describe '#revert_preparing_constraint_for_list_partitioning' do + it_behaves_like 'delegates to ConvertTableToFirstListPartition' do + let(:expected_method) { :revert_preparation_for_partitioning } + let(:migrate) do + migration.revert_preparing_constraint_for_list_partitioning(table_name: source_table, + partitioning_column: partition_column, + parent_table_name: partitioned_table, + initial_partitioning_value: min_date) + end + end + end + end + describe '#partition_table_by_date' do let(:partition_column) { 'created_at' } let(:old_primary_key) { 'id' } diff --git a/spec/lib/gitlab/database/postgres_constraint_spec.rb b/spec/lib/gitlab/database/postgres_constraint_spec.rb new file mode 100644 index 00000000000..75084a69115 --- /dev/null +++ b/spec/lib/gitlab/database/postgres_constraint_spec.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Database::PostgresConstraint, type: :model do + # PostgresConstraint does not `behaves_like 'a postgres model'` because it does not correspond 1-1 with a single entry + # in pg_class + let(:schema) { ActiveRecord::Base.connection.current_schema } + let(:table_name) { '_test_table' } + let(:table_identifier) { "#{schema}.#{table_name}" } + let(:referenced_name) { '_test_referenced' } + let(:check_constraint_a_positive) { 'check_constraint_a_positive' } + let(:check_constraint_a_gt_b) { 'check_constraint_a_gt_b' } + let(:invalid_constraint_a) { 'check_constraint_b_positive_invalid' } + let(:unique_constraint_a) { "#{table_name}_a_key" } + + before do + ActiveRecord::Base.connection.execute(<<~SQL) + create table #{referenced_name} ( + id bigserial primary key not null + ); + + create table #{table_name} ( + id bigserial not null, + referenced_id bigint not null references #{referenced_name}(id), + a integer unique, + b integer, + primary key (id, referenced_id), + constraint #{check_constraint_a_positive} check (a > 0), + constraint #{check_constraint_a_gt_b} check (a > b) + ); + + alter table #{table_name} add constraint #{invalid_constraint_a} CHECK (a > 1) NOT VALID; + SQL + end + + describe '#by_table_identifier' do + subject(:constraints_for_table) { described_class.by_table_identifier(table_identifier) } + + it 'includes all constraints on the table' do + all_constraints_for_table = described_class.all.to_a.select { |c| c.table_identifier == table_identifier } + expect(all_constraints_for_table.map(&:oid)).to match_array(constraints_for_table.pluck(:oid)) + end + + it 'throws an error if the format is incorrect' do + expect { described_class.by_table_identifier('not-an-identifier') }.to raise_error(ArgumentError) + end + end + + describe '#check_constraints' do + subject(:check_constraints) { described_class.check_constraints.by_table_identifier(table_identifier) } + + it 'finds check constraints for the table' do + expect(check_constraints.map(&:name)).to contain_exactly(check_constraint_a_positive, + check_constraint_a_gt_b, + invalid_constraint_a) + end + + it 'includes columns for the check constraints', :aggregate_failures do + expect(check_constraints.find_by(name: check_constraint_a_positive).column_names).to contain_exactly('a') + expect(check_constraints.find_by(name: check_constraint_a_gt_b).column_names).to contain_exactly('a', 'b') + end + end + + describe "#valid" do + subject(:valid_constraint_names) { described_class.valid.by_table_identifier(table_identifier).pluck(:name) } + + let(:all_constraint_names) { described_class.by_table_identifier(table_identifier).pluck(:name) } + + it 'excludes invalid constraints' do + expect(valid_constraint_names).not_to include(invalid_constraint_a) + expect(valid_constraint_names).to match_array(all_constraint_names - [invalid_constraint_a]) + end + end + + describe '#primary_key_constraints' do + subject(:pk_constraints) { described_class.primary_key_constraints.by_table_identifier(table_identifier) } + + it 'finds the primary key constraint for the table' do + expect(pk_constraints.count).to eq(1) + expect(pk_constraints.first.constraint_type).to eq('p') + end + + it 'finds the columns in the primary key constraint' do + constraint = pk_constraints.first + expect(constraint.column_names).to contain_exactly('id', 'referenced_id') + end + end + + describe '#unique_constraints' do + subject(:unique_constraints) { described_class.unique_constraints.by_table_identifier(table_identifier) } + + it 'finds the unique constraints for the table' do + expect(unique_constraints.pluck(:name)).to contain_exactly(unique_constraint_a) + end + end + + describe '#primary_or_unique_constraints' do + subject(:pk_or_unique_constraints) do + described_class.primary_or_unique_constraints.by_table_identifier(table_identifier) + end + + it 'finds primary and unique constraints' do + expect(pk_or_unique_constraints.pluck(:name)).to contain_exactly("#{table_name}_pkey", unique_constraint_a) + end + end + + describe '#including_column' do + it 'only matches constraints on the given column' do + constraints_on_a = described_class.by_table_identifier(table_identifier).including_column('a').map(&:name) + expect(constraints_on_a).to contain_exactly(check_constraint_a_positive, check_constraint_a_gt_b, + unique_constraint_a, invalid_constraint_a) + end + end + + describe '#not_including_column' do + it 'only matches constraints not including the given column' do + constraints_not_on_a = described_class.by_table_identifier(table_identifier).not_including_column('a').map(&:name) + + expect(constraints_not_on_a).to contain_exactly("#{table_name}_pkey", "#{table_name}_referenced_id_fkey") + end + end +end diff --git a/spec/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512_spec.rb b/spec/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512_spec.rb new file mode 100644 index 00000000000..df17d92bb0c --- /dev/null +++ b/spec/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::DoorkeeperSecretStoring::Secret::Pbkdf2Sha512 do + describe '.transform_secret' do + let(:plaintext_secret) { 'CzOBzBfU9F-HvsqfTaTXF4ivuuxYZuv3BoAK4pnvmyw' } + + it 'generates a PBKDF2+SHA512 hashed value in the correct format' do + expect(described_class.transform_secret(plaintext_secret)) + .to eq("$pbkdf2-sha512$20000$$.c0G5XJVEew1TyeJk5TrkvB0VyOaTmDzPrsdNRED9vVeZlSyuG3G90F0ow23zUCiWKAVwmNnR/ceh.nJG3MdpQ") # rubocop:disable Layout/LineLength + end + + context 'when hash_oauth_secrets is disabled' do + before do + stub_feature_flags(hash_oauth_secrets: false) + end + + it 'returns a plaintext secret' do + expect(described_class.transform_secret(plaintext_secret)).to eq(plaintext_secret) + end + end + end + + describe 'STRETCHES' do + it 'is 20_000' do + expect(described_class::STRETCHES).to eq(20_000) + end + end + + describe 'SALT' do + it 'is empty' do + expect(described_class::SALT).to be_empty + end + end + + describe '.secret_matches?' do + it "match by hashing the input if the stored value is hashed" do + stub_feature_flags(hash_oauth_secrets: false) + plain_secret = 'plain_secret' + stored_value = '$pbkdf2-sha512$20000$$/BwQRdwSpL16xkQhstavh7nvA5avCP7.4n9LLKe9AupgJDeA7M5xOAvG3N3E5XbRyGWWBbbr.BsojPVWzd1Sqg' # rubocop:disable Layout/LineLength + expect(described_class.secret_matches?(plain_secret, stored_value)).to be true + end + end +end diff --git a/spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb b/spec/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512_spec.rb similarity index 93% rename from spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb rename to spec/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512_spec.rb index e953733c997..c73744cd481 100644 --- a/spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb +++ b/spec/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::DoorkeeperSecretStoring::Pbkdf2Sha512 do +RSpec.describe Gitlab::DoorkeeperSecretStoring::Token::Pbkdf2Sha512 do describe '.transform_secret' do let(:plaintext_token) { 'CzOBzBfU9F-HvsqfTaTXF4ivuuxYZuv3BoAK4pnvmyw' } diff --git a/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb b/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb index 9266b1b0585..4e9208be985 100644 --- a/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb @@ -15,7 +15,8 @@ RSpec.describe Gitlab::GithubImport::Importer::ProtectedBranchesImporter do [ branch.new(name: 'main', protection: protection.new(enabled: false)), - branch.new(name: 'staging', protection: protection.new(enabled: true)) + branch.new(name: 'staging', protection: protection.new(enabled: true)), + branch.new(name: 'development', protection: nil) # when user has no admin right for this repo ] end @@ -154,12 +155,14 @@ RSpec.describe Gitlab::GithubImport::Importer::ProtectedBranchesImporter do let(:protection_struct) { Struct.new(:enabled, keyword_init: true) } let(:protected_branch) { branch_struct.new(name: 'main', protection: protection_struct.new(enabled: true)) } let(:unprotected_branch) { branch_struct.new(name: 'staging', protection: protection_struct.new(enabled: false)) } + # when user has no admin rights on repo + let(:unknown_protection_branch) { branch_struct.new(name: 'development', protection: nil) } let(:page_counter) { instance_double(Gitlab::GithubImport::PageCounter) } before do allow(client).to receive(:branches).with(project.import_source) - .and_return([protected_branch, unprotected_branch]) + .and_return([protected_branch, unprotected_branch, unknown_protection_branch]) allow(client).to receive(:branch_protection) .with(project.import_source, protected_branch.name).once .and_return(github_protection_rule) diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index e429fc93d65..b9c9e02625a 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -831,3 +831,17 @@ resource_state_events: - merge_request - source_merge_request - epic +iteration: + - group + - iterations_cadence + - issues + - labels + - merge_requests +resource_iteration_events: + - user + - issue + - merge_request + - iteration +iterations_cadence: + - group + - iterations diff --git a/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb b/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb index 9f1b15aa049..4ee825c71b6 100644 --- a/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb +++ b/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb @@ -79,14 +79,14 @@ RSpec.describe Gitlab::ImportExport::Base::RelationObjectSaver do let(:relation_definition) { { 'notes' => {} } } it 'saves valid subrelations and logs invalid subrelation' do - expect(relation_object.notes).to receive(:<<).and_call_original + expect(relation_object.notes).to receive(:<<).twice.and_call_original expect(Gitlab::Import::Logger) .to receive(:info) .with( message: '[Project/Group Import] Invalid subrelation', project_id: project.id, relation_key: 'issues', - error_messages: "Noteable can't be blank and Project does not match noteable project" + error_messages: "Project does not match noteable project" ) saver.execute @@ -94,9 +94,28 @@ RSpec.describe Gitlab::ImportExport::Base::RelationObjectSaver do issue = project.issues.last import_failure = project.import_failures.last + expect(invalid_note.persisted?).to eq(false) expect(issue.notes.count).to eq(5) expect(import_failure.source).to eq('RelationObjectSaver#save!') - expect(import_failure.exception_message).to eq("Noteable can't be blank and Project does not match noteable project") + expect(import_failure.exception_message).to eq('Project does not match noteable project') + end + + context 'when invalid subrelation can still be persisted' do + let(:relation_key) { 'merge_requests' } + let(:relation_definition) { { 'approvals' => {} } } + let(:approval_1) { build(:approval, merge_request_id: nil, user: create(:user)) } + let(:approval_2) { build(:approval, merge_request_id: nil, user: create(:user)) } + let(:relation_object) { build(:merge_request, source_project: project, target_project: project, approvals: [approval_1, approval_2]) } + + it 'saves the subrelation' do + expect(approval_1.valid?).to eq(false) + expect(Gitlab::Import::Logger).not_to receive(:info) + + saver.execute + + expect(project.merge_requests.first.approvals.count).to eq(2) + expect(project.merge_requests.first.approvals.first.persisted?).to eq(true) + end end context 'when importable is group' do diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index 352255afc8d..e591cbd05a0 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -930,3 +930,17 @@ ResourceStateEvent: - source_commit - close_after_error_tracking_resolve - close_auto_resolve_prometheus_alert +Iteration: + - created_at + - updated_at + - start_date + - due_date + - group_id + - iid + - description +ResourceIterationEvent: + - user_id + - created_at + - action +Iterations::Cadence: + - title diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb index e228a0a7d72..80ae5c6fd21 100644 --- a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb +++ b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb @@ -44,4 +44,18 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisMetric, :clean_git end end end + + context "with usage prefix disabled" do + let(:expected_value) { 3 } + + before do + 3.times do + Gitlab::UsageDataCounters::WebIdeCounter.increment_merge_requests_count + end + end + + it_behaves_like 'a correct instrumented metric value', { + options: { event: 'merge_requests_count', prefix: 'web_ide', include_usage_prefix: false } + } + end end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 7226c9db15d..181351222c1 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -266,13 +266,13 @@ RSpec.describe Ci::Runner do end shared_examples '.belonging_to_parent_group_of_project' do - let!(:group1) { create(:group) } - let!(:project1) { create(:project, group: group1) } - let!(:runner1) { create(:ci_runner, :group, groups: [group1]) } + let_it_be(:group1) { create(:group) } + let_it_be(:project1) { create(:project, group: group1) } + let_it_be(:runner1) { create(:ci_runner, :group, groups: [group1]) } - let!(:group2) { create(:group) } - let!(:project2) { create(:project, group: group2) } - let!(:runner2) { create(:ci_runner, :group, groups: [group2]) } + let_it_be(:group2) { create(:group) } + let_it_be(:project2) { create(:project, group: group2) } + let_it_be(:runner2) { create(:ci_runner, :group, groups: [group2]) } let(:project_id) { project1.id } @@ -495,8 +495,8 @@ RSpec.describe Ci::Runner do describe '.active' do subject { described_class.active(active_value) } - let!(:runner1) { create(:ci_runner, :instance, active: false) } - let!(:runner2) { create(:ci_runner, :instance) } + let_it_be(:runner1) { create(:ci_runner, :instance, active: false) } + let_it_be(:runner2) { create(:ci_runner, :instance) } context 'with active_value set to false' do let(:active_value) { false } @@ -544,7 +544,7 @@ RSpec.describe Ci::Runner do end describe '#stale?', :clean_gitlab_redis_cache do - let(:runner) { create(:ci_runner, :instance) } + let(:runner) { build(:ci_runner, :instance) } subject { runner.stale? } @@ -619,7 +619,7 @@ RSpec.describe Ci::Runner do end describe '#online?', :clean_gitlab_redis_cache do - let(:runner) { create(:ci_runner, :instance) } + let(:runner) { build(:ci_runner, :instance) } subject { runner.online? } @@ -1162,13 +1162,13 @@ RSpec.describe Ci::Runner do end describe '.assignable_for' do - let(:project) { create(:project) } - let(:group) { create(:group) } - let(:another_project) { create(:project) } - let!(:unlocked_project_runner) { create(:ci_runner, :project, projects: [project]) } - let!(:locked_project_runner) { create(:ci_runner, :project, locked: true, projects: [project]) } - let!(:group_runner) { create(:ci_runner, :group, groups: [group]) } - let!(:instance_runner) { create(:ci_runner, :instance) } + let_it_be(:project) { create(:project) } + let_it_be(:group) { create(:group) } + let_it_be(:another_project) { create(:project) } + let_it_be(:unlocked_project_runner) { create(:ci_runner, :project, projects: [project]) } + let_it_be(:locked_project_runner) { create(:ci_runner, :project, locked: true, projects: [project]) } + let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group]) } + let_it_be(:instance_runner) { create(:ci_runner, :instance) } context 'with already assigned project' do subject { described_class.assignable_for(project) } @@ -1186,78 +1186,74 @@ RSpec.describe Ci::Runner do end end - describe '#owner_project' do + context 'Project-related queries' do let_it_be(:project1) { create(:project) } let_it_be(:project2) { create(:project) } - subject(:owner_project) { project_runner.owner_project } + describe '#owner_project' do + subject(:owner_project) { project_runner.owner_project } - context 'with project1 as first project associated with runner' do - let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project1, project2]) } + context 'with project1 as first project associated with runner' do + let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project1, project2]) } - it { is_expected.to eq project1 } + it { is_expected.to eq project1 } + end + + context 'with project2 as first project associated with runner' do + let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project2, project1]) } + + it { is_expected.to eq project2 } + end end - context 'with project2 as first project associated with runner' do - let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project2, project1]) } + describe "belongs_to_one_project?" do + it "returns false if there are two projects runner is assigned to" do + runner = create(:ci_runner, :project, projects: [project1, project2]) - it { is_expected.to eq project2 } - end - end + expect(runner.belongs_to_one_project?).to be_falsey + end - describe "belongs_to_one_project?" do - it "returns false if there are two projects runner assigned to" do - project1 = create(:project) - project2 = create(:project) - runner = create(:ci_runner, :project, projects: [project1, project2]) + it "returns true if there is only one project runner is assigned to" do + runner = create(:ci_runner, :project, projects: [project1]) - expect(runner.belongs_to_one_project?).to be_falsey + expect(runner.belongs_to_one_project?).to be_truthy + end end - it "returns true" do - project = create(:project) - runner = create(:ci_runner, :project, projects: [project]) + describe '#belongs_to_more_than_one_project?' do + context 'project runner' do + context 'two projects assigned to runner' do + let(:runner) { create(:ci_runner, :project, projects: [project1, project2]) } - expect(runner.belongs_to_one_project?).to be_truthy - end - end + it 'returns true' do + expect(runner.belongs_to_more_than_one_project?).to be_truthy + end + end - describe '#belongs_to_more_than_one_project?' do - context 'project runner' do - let(:project1) { create(:project) } - let(:project2) { create(:project) } + context 'one project assigned to runner' do + let(:runner) { create(:ci_runner, :project, projects: [project1]) } - context 'two projects assigned to runner' do - let(:runner) { create(:ci_runner, :project, projects: [project1, project2]) } - - it 'returns true' do - expect(runner.belongs_to_more_than_one_project?).to be_truthy + it 'returns false' do + expect(runner.belongs_to_more_than_one_project?).to be_falsey + end end end - context 'one project assigned to runner' do - let(:runner) { create(:ci_runner, :project, projects: [project1]) } + context 'group runner' do + let(:group) { create(:group) } + let(:runner) { create(:ci_runner, :group, groups: [group]) } it 'returns false' do expect(runner.belongs_to_more_than_one_project?).to be_falsey end end - end - context 'group runner' do - let(:group) { create(:group) } - let(:runner) { create(:ci_runner, :group, groups: [group]) } + context 'shared runner' do + let(:runner) { create(:ci_runner, :instance) } - it 'returns false' do - expect(runner.belongs_to_more_than_one_project?).to be_falsey - end - end - - context 'shared runner' do - let(:runner) { create(:ci_runner, :instance) } - - it 'returns false' do - expect(runner.belongs_to_more_than_one_project?).to be_falsey + it 'returns false' do + expect(runner.belongs_to_more_than_one_project?).to be_falsey + end end end end @@ -1318,7 +1314,7 @@ RSpec.describe Ci::Runner do end describe '.search' do - let(:runner) { create(:ci_runner, token: '123abc', description: 'test runner') } + let_it_be(:runner) { create(:ci_runner, token: '123abc', description: 'test runner') } it 'returns runners with a matching token' do expect(described_class.search(runner.token)).to eq([runner]) @@ -1346,8 +1342,9 @@ RSpec.describe Ci::Runner do end describe '#pick_build!' do + let_it_be(:runner) { create(:ci_runner) } + let(:build) { create(:ci_build) } - let(:runner) { create(:ci_runner) } context 'runner can pick the build' do it 'calls #tick_runner_queue' do @@ -1384,26 +1381,26 @@ RSpec.describe Ci::Runner do end describe '.order_by' do + let_it_be(:runner1) { create(:ci_runner, created_at: 1.year.ago, contacted_at: 1.year.ago) } + let_it_be(:runner2) { create(:ci_runner, created_at: 1.month.ago, contacted_at: 1.month.ago) } + + before do + runner1.update!(token_expires_at: 1.year.from_now) + end + it 'supports ordering by the contact date' do - runner1 = create(:ci_runner, contacted_at: 1.year.ago) - runner2 = create(:ci_runner, contacted_at: 1.month.ago) runners = described_class.order_by('contacted_asc') expect(runners).to eq([runner1, runner2]) end it 'supports ordering by the creation date' do - runner1 = create(:ci_runner, created_at: 1.year.ago) - runner2 = create(:ci_runner, created_at: 1.month.ago) runners = described_class.order_by('created_asc') expect(runners).to eq([runner2, runner1]) end it 'supports ordering by the token expiration' do - runner1 = create(:ci_runner) - runner1.update!(token_expires_at: 1.year.from_now) - runner2 = create(:ci_runner) runner3 = create(:ci_runner) runner3.update!(token_expires_at: 1.month.from_now) diff --git a/spec/models/jira_connect_installation_spec.rb b/spec/models/jira_connect_installation_spec.rb index 3d1095845aa..9c1f7c678a9 100644 --- a/spec/models/jira_connect_installation_spec.rb +++ b/spec/models/jira_connect_installation_spec.rb @@ -45,4 +45,30 @@ RSpec.describe JiraConnectInstallation do expect(subject).to contain_exactly(subscription.installation) end end + + describe '#oauth_authorization_url' do + let_it_be(:installation) { create(:jira_connect_installation) } + + subject { installation.oauth_authorization_url } + + before do + allow(Gitlab).to receive_message_chain('config.gitlab.url') { 'http://test.host' } + end + + it { is_expected.to eq('http://test.host') } + + context 'with instance_url' do + let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'https://gitlab.example.com') } + + it { is_expected.to eq('https://gitlab.example.com') } + + context 'and jira_connect_oauth_self_managed feature is disabled' do + before do + stub_feature_flags(jira_connect_oauth_self_managed: false) + end + + it { is_expected.to eq('http://test.host') } + end + end + end end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index 5b032c53352..fefd9f71408 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -2729,6 +2729,41 @@ RSpec.describe ProjectPolicy do end end + describe 'read_milestone' do + context 'when project is public' do + let(:project) { public_project_in_group } + + context 'and issues and merge requests are private' do + before do + project.project_feature.update!( + issues_access_level: ProjectFeature::PRIVATE, + merge_requests_access_level: ProjectFeature::PRIVATE + ) + end + + context 'when user is an inherited member from the group' do + context 'and user is a guest' do + let(:current_user) { inherited_guest } + + it { is_expected.to be_allowed(:read_milestone) } + end + + context 'and user is a reporter' do + let(:current_user) { inherited_reporter } + + it { is_expected.to be_allowed(:read_milestone) } + end + + context 'and user is a developer' do + let(:current_user) { inherited_developer } + + it { is_expected.to be_allowed(:read_milestone) } + end + end + end + end + end + private def project_subject(project_type) diff --git a/spec/services/ci/create_pipeline_service/partitioning_spec.rb b/spec/services/ci/create_pipeline_service/partitioning_spec.rb index cb4f32f591f..43fbb74ede4 100644 --- a/spec/services/ci/create_pipeline_service/partitioning_spec.rb +++ b/spec/services/ci/create_pipeline_service/partitioning_spec.rb @@ -96,6 +96,47 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes end end + context 'with parent child pipelines' do + before do + allow(Ci::Pipeline) + .to receive(:current_partition_value) + .and_return(current_partition_id, 301, 302) + + allow_next_found_instance_of(Ci::Bridge) do |bridge| + allow(bridge).to receive(:yaml_for_downstream).and_return(child_config) + end + end + + let(:config) do + <<-YAML + test: + trigger: + include: child.yml + YAML + end + + let(:child_config) do + <<-YAML + test: + script: make test + YAML + end + + it 'assigns partition values to child pipelines', :aggregate_failures, :sidekiq_inline do + expect(pipeline).to be_created_successfully + expect(pipeline.child_pipelines).to all be_created_successfully + + child_partition_ids = pipeline.child_pipelines.map(&:partition_id).uniq + child_jobs = CommitStatus.where(commit_id: pipeline.child_pipelines) + + expect(pipeline.partition_id).to eq(current_partition_id) + expect(child_partition_ids).to eq([current_partition_id]) + + expect(child_jobs).to all be_a(Ci::Build) + expect(child_jobs.pluck(:partition_id).uniq).to eq([current_partition_id]) + end + end + def find_metadata(name) pipeline .processables diff --git a/spec/services/issues/relative_position_rebalancing_service_spec.rb b/spec/services/issues/relative_position_rebalancing_service_spec.rb index 20064bd7e4b..37a94e1d6a2 100644 --- a/spec/services/issues/relative_position_rebalancing_service_spec.rb +++ b/spec/services/issues/relative_position_rebalancing_service_spec.rb @@ -72,22 +72,8 @@ RSpec.describe Issues::RelativePositionRebalancingService, :clean_gitlab_redis_s end.not_to change { issues_in_position_order.map(&:id) } end - it 'does nothing if the feature flag is disabled' do - stub_feature_flags(rebalance_issues: false) - issue = project.issues.first - issue.project - issue.project.group - old_pos = issue.relative_position - - # fetching root namespace in the initializer triggers 2 queries: - # for fetching a random project from collection and fetching the root namespace. - expect { service.execute }.not_to exceed_query_limit(2) - expect(old_pos).to eq(issue.reload.relative_position) - end - it 'acts if the flag is enabled for the root namespace' do issue = create(:issue, project: project, author: user, relative_position: max_pos) - stub_feature_flags(rebalance_issues: project.root_namespace) expect { service.execute }.to change { issue.reload.relative_position } end @@ -95,7 +81,6 @@ RSpec.describe Issues::RelativePositionRebalancingService, :clean_gitlab_redis_s it 'acts if the flag is enabled for the group' do issue = create(:issue, project: project, author: user, relative_position: max_pos) project.update!(group: create(:group)) - stub_feature_flags(rebalance_issues: issue.project.group) expect { service.execute }.to change { issue.reload.relative_position } end diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 68d145259b5..8a2e9ed74f7 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -304,38 +304,6 @@ RSpec.describe Issues::UpdateService, :mailer do end end - it 'does not rebalance even if needed if the flag is disabled' do - stub_feature_flags(rebalance_issues: false) - - range = described_class::NO_REBALANCING_NEEDED - issue1 = create(:issue, project: project, relative_position: range.first - 100) - issue2 = create(:issue, project: project, relative_position: range.first) - issue.update!(relative_position: RelativePositioning::START_POSITION) - - opts[:move_between_ids] = [issue1.id, issue2.id] - - expect(Issues::RebalancingWorker).not_to receive(:perform_async) - - update_issue(opts) - expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position) - end - - it 'rebalances if needed if the flag is enabled for the project' do - stub_feature_flags(rebalance_issues: project) - - range = described_class::NO_REBALANCING_NEEDED - issue1 = create(:issue, project: project, relative_position: range.first - 100) - issue2 = create(:issue, project: project, relative_position: range.first) - issue.update!(relative_position: RelativePositioning::START_POSITION) - - opts[:move_between_ids] = [issue1.id, issue2.id] - - expect(Issues::RebalancingWorker).to receive(:perform_async).with(nil, nil, project.root_namespace.id) - - update_issue(opts) - expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position) - end - it 'rebalances if needed on the left' do range = described_class::NO_REBALANCING_NEEDED issue1 = create(:issue, project: project, relative_position: range.first - 100) diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb index 7b60561432c..d8c9c5b7556 100644 --- a/spec/support/helpers/usage_data_helpers.rb +++ b/spec/support/helpers/usage_data_helpers.rb @@ -11,10 +11,6 @@ module UsageDataHelpers wiki_pages_create wiki_pages_update wiki_pages_delete - web_ide_views - web_ide_commits - web_ide_merge_requests - web_ide_previews navbar_searches cycle_analytics_views productivity_analytics_views diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb index 1d4731d9b39..fc7255a4a20 100644 --- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb @@ -6,13 +6,19 @@ RSpec.shared_context 'ProjectPolicy context' do let_it_be(:reporter) { create(:user) } let_it_be(:developer) { create(:user) } let_it_be(:maintainer) { create(:user) } + let_it_be(:inherited_guest) { create(:user) } + let_it_be(:inherited_reporter) { create(:user) } + let_it_be(:inherited_developer) { create(:user) } + let_it_be(:inherited_maintainer) { create(:user) } let_it_be(:owner) { create(:user) } let_it_be(:admin) { create(:admin) } let_it_be(:non_member) { create(:user) } + let_it_be_with_refind(:group) { create(:group, :public) } let_it_be_with_refind(:private_project) { create(:project, :private, namespace: owner.namespace) } let_it_be_with_refind(:internal_project) { create(:project, :internal, namespace: owner.namespace) } let_it_be_with_refind(:public_project) { create(:project, :public, namespace: owner.namespace) } - let_it_be_with_refind(:public_project_in_group) { create(:project, :public, namespace: create(:group, :public)) } + let_it_be_with_refind(:public_project_in_group) { create(:project, :public, namespace: group) } + let_it_be_with_refind(:private_project_in_group) { create(:project, :private, namespace: group) } let(:base_guest_permissions) do %i[ @@ -95,6 +101,11 @@ RSpec.shared_context 'ProjectPolicy context' do let(:owner_permissions) { base_owner_permissions + additional_owner_permissions } before_all do + group.add_guest(inherited_guest) + group.add_reporter(inherited_reporter) + group.add_developer(inherited_developer) + group.add_maintainer(inherited_maintainer) + [private_project, internal_project, public_project, public_project_in_group].each do |project| project.add_guest(guest) project.add_reporter(reporter) diff --git a/spec/support/shared_examples/features/manage_applications_shared_examples.rb b/spec/support/shared_examples/features/manage_applications_shared_examples.rb index 442264e7ae4..b59f3f1e27b 100644 --- a/spec/support/shared_examples/features/manage_applications_shared_examples.rb +++ b/spec/support/shared_examples/features/manage_applications_shared_examples.rb @@ -5,39 +5,87 @@ RSpec.shared_examples 'manage applications' do let_it_be(:application_name_changed) { "#{application_name} changed" } let_it_be(:application_redirect_uri) { 'https://foo.bar' } - it 'allows user to manage applications', :js do - visit new_application_path - - expect(page).to have_content 'Add new application' - - fill_in :doorkeeper_application_name, with: application_name - fill_in :doorkeeper_application_redirect_uri, with: application_redirect_uri - check :doorkeeper_application_scopes_read_user - click_on 'Save application' - - validate_application(application_name, 'Yes') - expect(page).to have_link('Continue', href: index_path) - - application = Doorkeeper::Application.find_by(name: application_name) - expect(page).to have_css("button[title=\"Copy secret\"][data-clipboard-text=\"#{application.secret}\"]", text: 'Copy') - - click_on 'Edit' - - application_name_changed = "#{application_name} changed" - - fill_in :doorkeeper_application_name, with: application_name_changed - uncheck :doorkeeper_application_confidential - click_on 'Save application' - - validate_application(application_name_changed, 'No') - expect(page).not_to have_link('Continue') - - visit_applications_path - - page.within '.oauth-applications' do - click_on 'Destroy' + context 'when hash_oauth_secrets flag set' do + before do + stub_feature_flags(hash_oauth_secrets: true) + end + + it 'allows user to manage applications', :js do + visit new_application_path + + expect(page).to have_content 'Add new application' + + fill_in :doorkeeper_application_name, with: application_name + fill_in :doorkeeper_application_redirect_uri, with: application_redirect_uri + check :doorkeeper_application_scopes_read_user + click_on 'Save application' + + validate_application(application_name, 'Yes') + expect(page).to have_content _('This is the only time the secret is accessible. Copy the secret and store it securely') + expect(page).to have_link('Continue', href: index_path) + + expect(page).to have_css("button[title=\"Copy secret\"]", text: 'Copy') + + click_on 'Edit' + + application_name_changed = "#{application_name} changed" + + fill_in :doorkeeper_application_name, with: application_name_changed + uncheck :doorkeeper_application_confidential + click_on 'Save application' + + validate_application(application_name_changed, 'No') + expect(page).not_to have_link('Continue') + expect(page).to have_content _('The secret is only available when you first create the application') + + visit_applications_path + + page.within '.oauth-applications' do + click_on 'Destroy' + end + expect(page.find('.oauth-applications')).not_to have_content 'test_changed' + end + end + + context 'when hash_oauth_secrets flag not set' do + before do + stub_feature_flags(hash_oauth_secrets: false) + end + + it 'allows user to manage applications', :js do + visit new_application_path + + expect(page).to have_content 'Add new application' + + fill_in :doorkeeper_application_name, with: application_name + fill_in :doorkeeper_application_redirect_uri, with: application_redirect_uri + check :doorkeeper_application_scopes_read_user + click_on 'Save application' + + validate_application(application_name, 'Yes') + expect(page).to have_link('Continue', href: index_path) + + application = Doorkeeper::Application.find_by(name: application_name) + expect(page).to have_css("button[title=\"Copy secret\"][data-clipboard-text=\"#{application.secret}\"]", text: 'Copy') + + click_on 'Edit' + + application_name_changed = "#{application_name} changed" + + fill_in :doorkeeper_application_name, with: application_name_changed + uncheck :doorkeeper_application_confidential + click_on 'Save application' + + validate_application(application_name_changed, 'No') + expect(page).not_to have_link('Continue') + + visit_applications_path + + page.within '.oauth-applications' do + click_on 'Destroy' + end + expect(page.find('.oauth-applications')).not_to have_content 'test_changed' end - expect(page.find('.oauth-applications')).not_to have_content 'test_changed' end context 'when scopes are blank' do diff --git a/spec/support/shared_examples/policies/project_policy_shared_examples.rb b/spec/support/shared_examples/policies/project_policy_shared_examples.rb index c4083df47e2..cfcc3615e13 100644 --- a/spec/support/shared_examples/policies/project_policy_shared_examples.rb +++ b/spec/support/shared_examples/policies/project_policy_shared_examples.rb @@ -107,70 +107,88 @@ RSpec.shared_examples 'deploy token does not get confused with user' do end RSpec.shared_examples 'project policies as guest' do - context 'abilities for public projects' do - let(:project) { public_project } - let(:current_user) { guest } + let(:reporter_public_build_permissions) do + reporter_permissions - [:read_build, :read_pipeline] + end - it do - expect_allowed(*guest_permissions) - expect_allowed(*public_permissions) - expect_disallowed(*developer_permissions) - expect_disallowed(*maintainer_permissions) - expect_disallowed(*owner_permissions) + context 'as a direct project member' do + context 'abilities for public projects' do + let(:project) { public_project } + let(:current_user) { guest } + + specify do + expect_allowed(*guest_permissions) + expect_allowed(*public_permissions) + expect_disallowed(*developer_permissions) + expect_disallowed(*maintainer_permissions) + expect_disallowed(*owner_permissions) + end + end + + context 'abilities for non-public projects' do + let(:project) { private_project } + let(:current_user) { guest } + + specify do + expect_allowed(*guest_permissions) + expect_disallowed(*reporter_public_build_permissions) + expect_disallowed(*team_member_reporter_permissions) + expect_disallowed(*developer_permissions) + expect_disallowed(*maintainer_permissions) + expect_disallowed(*owner_permissions) + end + + it_behaves_like 'deploy token does not get confused with user' do + let(:user_id) { guest.id } + end + + it_behaves_like 'archived project policies' do + let(:regular_abilities) { guest_permissions } + end + + context 'public builds enabled' do + specify do + expect_allowed(*guest_permissions) + expect_allowed(:read_build, :read_pipeline) + end + end + + context 'when public builds disabled' do + before do + project.update!(public_builds: false) + end + + specify do + expect_allowed(*guest_permissions) + expect_disallowed(:read_build, :read_pipeline) + end + end + + context 'when builds are disabled' do + before do + project.project_feature.update!(builds_access_level: ProjectFeature::DISABLED) + end + + specify do + expect_disallowed(:read_build) + expect_allowed(:read_pipeline) + end + end end end - context 'abilities for non-public projects' do - let(:project) { private_project } - let(:current_user) { guest } + context 'as an inherited member from the group' do + context 'abilities for private projects' do + let(:project) { private_project_in_group } + let(:current_user) { inherited_guest } - let(:reporter_public_build_permissions) do - reporter_permissions - [:read_build, :read_pipeline] - end - - it do - expect_allowed(*guest_permissions) - expect_disallowed(*reporter_public_build_permissions) - expect_disallowed(*team_member_reporter_permissions) - expect_disallowed(*developer_permissions) - expect_disallowed(*maintainer_permissions) - expect_disallowed(*owner_permissions) - end - - it_behaves_like 'deploy token does not get confused with user' do - let(:user_id) { guest.id } - end - - it_behaves_like 'archived project policies' do - let(:regular_abilities) { guest_permissions } - end - - context 'public builds enabled' do - it do + specify do expect_allowed(*guest_permissions) - expect_allowed(:read_build, :read_pipeline) - end - end - - context 'when public builds disabled' do - before do - project.update!(public_builds: false) - end - - it do - expect_allowed(*guest_permissions) - expect_disallowed(:read_build, :read_pipeline) - end - end - - context 'when builds are disabled' do - before do - project.project_feature.update!(builds_access_level: ProjectFeature::DISABLED) - end - - it do - expect_disallowed(:read_build) - expect_allowed(:read_pipeline) + expect_disallowed(*reporter_public_build_permissions) + expect_disallowed(*team_member_reporter_permissions) + expect_disallowed(*developer_permissions) + expect_disallowed(*maintainer_permissions) + expect_disallowed(*owner_permissions) end end end @@ -181,7 +199,7 @@ RSpec.shared_examples 'project policies as reporter' do let(:project) { private_project } let(:current_user) { reporter } - it do + specify do expect_allowed(*guest_permissions) expect_allowed(*reporter_permissions) expect_allowed(*team_member_reporter_permissions) @@ -198,6 +216,22 @@ RSpec.shared_examples 'project policies as reporter' do let(:regular_abilities) { reporter_permissions } end end + + context 'as an inherited member from the group' do + context 'abilities for private projects' do + let(:project) { private_project_in_group } + let(:current_user) { inherited_reporter } + + specify do + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*team_member_reporter_permissions) + expect_disallowed(*developer_permissions) + expect_disallowed(*maintainer_permissions) + expect_disallowed(*owner_permissions) + end + end + end end RSpec.shared_examples 'project policies as developer' do @@ -205,7 +239,7 @@ RSpec.shared_examples 'project policies as developer' do let(:project) { private_project } let(:current_user) { developer } - it do + specify do expect_allowed(*guest_permissions) expect_allowed(*reporter_permissions) expect_allowed(*team_member_reporter_permissions) @@ -222,6 +256,22 @@ RSpec.shared_examples 'project policies as developer' do let(:regular_abilities) { developer_permissions } end end + + context 'as an inherited member from the group' do + context 'abilities for private projects' do + let(:project) { private_project_in_group } + let(:current_user) { inherited_developer } + + specify do + expect_allowed(*guest_permissions) + expect_allowed(*reporter_permissions) + expect_allowed(*team_member_reporter_permissions) + expect_allowed(*developer_permissions) + expect_disallowed(*maintainer_permissions) + expect_disallowed(*owner_permissions) + end + end + end end RSpec.shared_examples 'project policies as maintainer' do diff --git a/spec/support/shared_examples/requests/applications_controller_shared_examples.rb b/spec/support/shared_examples/requests/applications_controller_shared_examples.rb index 8f852d42c2c..642930dd982 100644 --- a/spec/support/shared_examples/requests/applications_controller_shared_examples.rb +++ b/spec/support/shared_examples/requests/applications_controller_shared_examples.rb @@ -11,6 +11,7 @@ RSpec.shared_examples 'applications controller - GET #show' do context 'when application is viewed after being created' do before do create_application + stub_feature_flags(hash_oauth_secrets: false) end it 'sets `@created` instance variable to `true`' do @@ -21,6 +22,10 @@ RSpec.shared_examples 'applications controller - GET #show' do end context 'when application is reviewed' do + before do + stub_feature_flags(hash_oauth_secrets: false) + end + it 'sets `@created` instance variable to `false`' do get show_path @@ -32,6 +37,7 @@ end RSpec.shared_examples 'applications controller - POST #create' do it "sets `#{OauthApplications::CREATED_SESSION_KEY}` session key to `true`" do + stub_feature_flags(hash_oauth_secrets: false) create_application expect(session[OauthApplications::CREATED_SESSION_KEY]).to eq(true)