diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-28 21:09:03 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-28 21:09:03 +0300 |
commit | b420660ef1369fec4d09b7bf5e961d81980974e5 (patch) | |
tree | f096fdd1bd3b4898e1b2ca80957ce68c200c09f0 /app | |
parent | b8026fd558e7ec154c626208a33c1485aec8f4ea (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
41 files changed, 254 insertions, 67 deletions
diff --git a/app/assets/javascripts/pipelines/components/pipeline_tabs.vue b/app/assets/javascripts/pipelines/components/pipeline_tabs.vue index e1745969649..df59962569e 100644 --- a/app/assets/javascripts/pipelines/components/pipeline_tabs.vue +++ b/app/assets/javascripts/pipelines/components/pipeline_tabs.vue @@ -34,7 +34,13 @@ export default { PipelineGraphWrapper, TestReports, }, - inject: ['defaultTabValue', 'failedJobsCount', 'failedJobsSummary', 'totalJobCount'], + inject: [ + 'defaultTabValue', + 'failedJobsCount', + 'failedJobsSummary', + 'totalJobCount', + 'testsCount', + ], computed: { showFailedJobsTab() { return this.failedJobsCount > 0; @@ -81,11 +87,11 @@ export default { </template> <failed-jobs-app :failed-jobs-summary="failedJobsSummary" /> </gl-tab> - <gl-tab - :title="$options.i18n.tabs.testsTitle" - :active="isActive($options.tabNames.tests)" - data-testid="tests-tab" - > + <gl-tab :active="isActive($options.tabNames.tests)" data-testid="tests-tab" lazy> + <template #title> + <span class="gl-mr-2">{{ $options.i18n.tabs.testsTitle }}</span> + <gl-badge size="sm" data-testid="tests-counter">{{ testsCount }}</gl-badge> + </template> <test-reports /> </gl-tab> <slot></slot> diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue b/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue index 13e942e55d3..e5666f7a658 100644 --- a/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue +++ b/app/assets/javascripts/pipelines/components/test_reports/test_reports.vue @@ -1,6 +1,7 @@ <script> import { GlLoadingIcon } from '@gitlab/ui'; import { mapActions, mapGetters, mapState } from 'vuex'; +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import createTestReportsStore from '../../stores/test_reports'; import EmptyState from './empty_state.vue'; import TestSuiteTable from './test_suite_table.vue'; @@ -16,6 +17,7 @@ export default { TestSummary, TestSummaryTable, }, + mixins: [glFeatureFlagMixin()], inject: ['blobPath', 'summaryEndpoint', 'suiteEndpoint'], computed: { ...mapState('testReports', ['isLoading', 'selectedSuiteIndex', 'testReports']), @@ -29,14 +31,16 @@ export default { }, }, created() { - this.$store.registerModule( - 'testReports', - createTestReportsStore({ - blobPath: this.blobPath, - summaryEndpoint: this.summaryEndpoint, - suiteEndpoint: this.suiteEndpoint, - }), - ); + if (!this.glFeatures.pipelineTabsVue) { + this.$store.registerModule( + 'testReports', + createTestReportsStore({ + blobPath: this.blobPath, + summaryEndpoint: this.summaryEndpoint, + suiteEndpoint: this.suiteEndpoint, + }), + ); + } this.fetchSummary(); }, diff --git a/app/assets/javascripts/pipelines/pipeline_tabs.js b/app/assets/javascripts/pipelines/pipeline_tabs.js index 8f2fc9b189e..7051d356089 100644 --- a/app/assets/javascripts/pipelines/pipeline_tabs.js +++ b/app/assets/javascripts/pipelines/pipeline_tabs.js @@ -5,6 +5,7 @@ import PipelineTabs from 'ee_else_ce/pipelines/components/pipeline_tabs.vue'; import { removeParams, updateHistory } from '~/lib/utils/url_utility'; import { TAB_QUERY_PARAM } from '~/pipelines/constants'; import { parseBoolean } from '~/lib/utils/common_utils'; +import createTestReportsStore from './stores/test_reports'; import { getPipelineDefaultTab, reportToSentry } from './utils'; Vue.use(VueApollo); @@ -33,6 +34,13 @@ export const createAppOptions = (selector, apolloProvider) => { licenseManagementSettingsPath, licensesApiPath, canManageLicenses, + summaryEndpoint, + suiteEndpoint, + blobPath, + hasTestReport, + emptyStateImagePath, + artifactsExpiredImagePath, + testsCount, } = dataset; const defaultTabValue = getPipelineDefaultTab(window.location.href); @@ -43,7 +51,15 @@ export const createAppOptions = (selector, apolloProvider) => { PipelineTabs, }, apolloProvider, - store: new Vuex.Store(), + store: new Vuex.Store({ + modules: { + testReports: createTestReportsStore({ + blobPath, + summaryEndpoint, + suiteEndpoint, + }), + }, + }), provide: { canGenerateCodequalityReports: parseBoolean(canGenerateCodequalityReports), codequalityReportDownloadPath, @@ -62,6 +78,13 @@ export const createAppOptions = (selector, apolloProvider) => { licenseManagementSettingsPath, licensesApiPath, canManageLicenses: parseBoolean(canManageLicenses), + summaryEndpoint, + suiteEndpoint, + blobPath, + hasTestReport, + emptyStateImagePath, + artifactsExpiredImagePath, + testsCount, }, errorCaptured(err, _vm, info) { reportToSentry('pipeline_tabs', `error: ${err}, info: ${info}`); diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 32d1ddf920e..6d1ffc1f2e8 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -5,6 +5,7 @@ class AutocompleteController < ApplicationController skip_before_action :authenticate_user!, only: [:users, :award_emojis, :merge_request_target_branches] before_action :check_search_rate_limit!, only: [:users, :projects] + before_action :authorize_admin_project, only: :deploy_keys_with_owners feature_category :users, [:users, :user] feature_category :projects, [:projects] @@ -69,6 +70,10 @@ class AutocompleteController < ApplicationController private + def authorize_admin_project + render_403 unless Ability.allowed?(current_user, :admin_project, project) + end + def project @project ||= Autocomplete::ProjectFinder .new(current_user, params) diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 8ecf0c158e0..47b2a460e6f 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -483,10 +483,14 @@ class IssuableFinder end def by_crm_contact(items) + return items unless can_filter_by_crm_contact? + Issuables::CrmContactFilter.new(params: original_params).filter(items) end def by_crm_organization(items) + return items unless can_filter_by_crm_organization? + Issuables::CrmOrganizationFilter.new(params: original_params).filter(items) end @@ -499,4 +503,20 @@ class IssuableFinder def feature_flag_scope params.group || params.project end + + def can_filter_by_crm_contact? + current_user&.can?(:read_crm_contact, root_group) + end + + def can_filter_by_crm_organization? + current_user&.can?(:read_crm_organization, root_group) + end + + def root_group + strong_memoize(:root_group) do + base_group = params.group || params.project&.group + + base_group&.root_ancestor + end + end end diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb index b399f0490ee..c0e063a34d5 100644 --- a/app/graphql/gitlab_schema.rb +++ b/app/graphql/gitlab_schema.rb @@ -53,6 +53,7 @@ class GitlabSchema < GraphQL::Schema def get_type(type_name, context = GraphQL::Query::NullContext) type_name = Gitlab::GlobalId::Deprecations.apply_to_graphql_name(type_name) + type_name = Gitlab::Graphql::TypeNameDeprecations.apply_to_graphql_name(type_name) super(type_name, context) end @@ -163,6 +164,7 @@ class GitlabSchema < GraphQL::Schema def get_type(type_name) type_name = Gitlab::GlobalId::Deprecations.apply_to_graphql_name(type_name) + type_name = Gitlab::Graphql::TypeNameDeprecations.apply_to_graphql_name(type_name) super(type_name) end diff --git a/app/graphql/types/global_id_type.rb b/app/graphql/types/global_id_type.rb index 145a5a22460..a71c2fb0e6c 100644 --- a/app/graphql/types/global_id_type.rb +++ b/app/graphql/types/global_id_type.rb @@ -50,7 +50,7 @@ module Types #{ if deprecation = Gitlab::GlobalId::Deprecations.deprecation_by(model_name) 'The older format `"' + - ::Gitlab::GlobalId.build(model_name: deprecation.old_model_name, id: 1).to_s + + ::Gitlab::GlobalId.build(model_name: deprecation.old_name, id: 1).to_s + '"` was deprecated in ' + deprecation.milestone + '.' end} diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 56574062a11..823a0b28a79 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -18,11 +18,31 @@ module MergeRequestsHelper classes.join(' ') end - def merge_path_description(merge_request, separator) + def merge_path_description(merge_request, with_arrow: false) if merge_request.for_fork? - "Project:Branches: #{@merge_request.source_project_path}:#{@merge_request.source_branch} #{separator} #{@merge_request.target_project.full_path}:#{@merge_request.target_branch}" + msg = if with_arrow + _("Project:Branches: %{source_project_path}:%{source_branch} → %{target_project_path}:%{target_branch}") + else + _("Project:Branches: %{source_project_path}:%{source_branch} to %{target_project_path}:%{target_branch}") + end + + msg % { + source_project_path: merge_request.source_project_path, + source_branch: merge_request.source_branch, + target_project_path: merge_request.target_project.full_path, + target_branch: merge_request.target_branch + } else - "Branches: #{@merge_request.source_branch} #{separator} #{@merge_request.target_branch}" + msg = if with_arrow + _("Branches: %{source_branch} → %{target_branch}") + else + _("Branches: %{source_branch} to %{target_branch}") + end + + msg % { + source_branch: merge_request.source_branch, + target_branch: merge_request.target_branch + } end end diff --git a/app/helpers/projects/pipeline_helper.rb b/app/helpers/projects/pipeline_helper.rb index 3b3fe13e58a..5f2a9f7bf21 100644 --- a/app/helpers/projects/pipeline_helper.rb +++ b/app/helpers/projects/pipeline_helper.rb @@ -14,7 +14,14 @@ module Projects metrics_path: namespace_project_ci_prometheus_metrics_histograms_path(namespace_id: project.namespace, project_id: project, format: :json), pipeline_iid: pipeline.iid, pipeline_project_path: project.full_path, - total_job_count: pipeline.total_size + total_job_count: pipeline.total_size, + summary_endpoint: summary_project_pipeline_tests_path(project, pipeline, format: :json), + suite_endpoint: project_pipeline_test_path(project, pipeline, suite_name: 'suite', format: :json), + blob_path: project_blob_path(project, pipeline.sha), + has_test_report: pipeline.has_test_reports?, + empty_state_image_path: image_path('illustrations/empty-state/empty-test-cases-lg.svg'), + artifacts_expired_image_path: image_path('illustrations/pipeline.svg'), + tests_count: pipeline.test_report_summary.total[:count] } end end diff --git a/app/models/error_tracking/project_error_tracking_setting.rb b/app/models/error_tracking/project_error_tracking_setting.rb index 30382a1c205..4953f24755c 100644 --- a/app/models/error_tracking/project_error_tracking_setting.rb +++ b/app/models/error_tracking/project_error_tracking_setting.rb @@ -44,6 +44,8 @@ module ErrorTracking key: Settings.attr_encrypted_db_key_base_32, algorithm: 'aes-256-gcm' + before_validation :reset_token + after_save :clear_reactive_cache! # When a user enables the integrated error tracking @@ -182,6 +184,12 @@ module ErrorTracking private + def reset_token + if api_url_changed? && !encrypted_token_changed? + self.token = nil + end + end + def ensure_issue_belongs_to_project!(project_id_from_api) raise 'The Sentry issue appers to be outside of the configured Sentry project' if Integer(project_id_from_api) != ensure_sentry_project_id! end diff --git a/app/models/grafana_integration.rb b/app/models/grafana_integration.rb index 00213732fee..0358e37c58b 100644 --- a/app/models/grafana_integration.rb +++ b/app/models/grafana_integration.rb @@ -18,6 +18,8 @@ class GrafanaIntegration < ApplicationRecord validates :enabled, inclusion: { in: [true, false] } + before_validation :reset_token + scope :enabled, -> { where(enabled: true) } def client @@ -36,6 +38,12 @@ class GrafanaIntegration < ApplicationRecord private + def reset_token + if grafana_url_changed? && !encrypted_token_changed? + self.token = nil + end + end + def token decrypt(:token, encrypted_token) end diff --git a/app/models/hooks/web_hook_log.rb b/app/models/hooks/web_hook_log.rb index 2f03b3591cf..24e5f193a32 100644 --- a/app/models/hooks/web_hook_log.rb +++ b/app/models/hooks/web_hook_log.rb @@ -22,6 +22,7 @@ class WebHookLog < ApplicationRecord validates :web_hook, presence: true before_save :obfuscate_basic_auth + before_save :redact_author_email def self.recent where('created_at >= ?', 2.days.ago.beginning_of_day) @@ -52,4 +53,10 @@ class WebHookLog < ApplicationRecord def obfuscate_basic_auth self.url = safe_url end + + def redact_author_email + return unless self.request_data.dig('commit', 'author', 'email').present? + + self.request_data['commit']['author']['email'] = _('[REDACTED]') + end end diff --git a/app/models/integrations/campfire.rb b/app/models/integrations/campfire.rb index bf1358ac0f6..3f7fa1c51b2 100644 --- a/app/models/integrations/campfire.rb +++ b/app/models/integrations/campfire.rb @@ -2,7 +2,15 @@ module Integrations class Campfire < Integration + SUBDOMAIN_REGEXP = %r{\A[a-z](?:[a-z0-9-]*[a-z0-9])?\z}i.freeze + validates :token, presence: true, if: :activated? + validates :room, + allow_blank: true, + numericality: { only_integer: true, greater_than: 0 } + validates :subdomain, + allow_blank: true, + format: { with: SUBDOMAIN_REGEXP }, length: { in: 1..63 } field :token, type: 'password', @@ -16,6 +24,7 @@ module Integrations field :subdomain, title: -> { _('Campfire subdomain (optional)') }, placeholder: '', + exposes_secrets: true, help: -> do ERB::Util.html_escape( s_('CampfireService|The %{code_open}.campfirenow.com%{code_close} subdomain.') diff --git a/app/models/integrations/drone_ci.rb b/app/models/integrations/drone_ci.rb index b1f72b7144e..de69afeba6a 100644 --- a/app/models/integrations/drone_ci.rb +++ b/app/models/integrations/drone_ci.rb @@ -13,6 +13,7 @@ module Integrations field :drone_url, title: -> { s_('ProjectService|Drone server URL') }, placeholder: 'http://drone.example.com', + exposes_secrets: true, required: true field :token, diff --git a/app/models/integrations/jira.rb b/app/models/integrations/jira.rb index c9c9b9d59d6..566bbc456f8 100644 --- a/app/models/integrations/jira.rb +++ b/app/models/integrations/jira.rb @@ -223,7 +223,9 @@ module Integrations # support any events. end - def find_issue(issue_key, rendered_fields: false, transitions: false) + def find_issue(issue_key, rendered_fields: false, transitions: false, restrict_project_key: false) + return if restrict_project_key && parse_project_from_issue_key(issue_key) != project_key + expands = [] expands << 'renderedFields' if rendered_fields expands << 'transitions' if transitions @@ -321,6 +323,10 @@ module Integrations private + def parse_project_from_issue_key(issue_key) + issue_key.gsub(Gitlab::Regex.jira_issue_key_project_key_extraction_regex, '') + end + def branch_name(commit) commit.first_ref_by_oid(project.repository) end diff --git a/app/models/integrations/packagist.rb b/app/models/integrations/packagist.rb index 05ee919892d..502e509f7b9 100644 --- a/app/models/integrations/packagist.rb +++ b/app/models/integrations/packagist.rb @@ -6,14 +6,14 @@ module Integrations extend Gitlab::Utils::Override field :username, - title: -> { _('Username') }, + title: -> { s_('Username') }, help: -> { s_('Enter your Packagist username.') }, placeholder: '', required: true field :token, type: 'password', - title: -> { _('Token') }, + title: -> { s_('Token') }, help: -> { s_('Enter your Packagist token.') }, non_empty_password_title: -> { s_('ProjectService|Enter new token') }, non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') }, @@ -21,9 +21,11 @@ module Integrations required: true field :server, - title: -> { _('Server (optional)') }, + title: -> { s_('Server (optional)') }, help: -> { s_('Enter your Packagist server. Defaults to https://packagist.org.') }, - placeholder: 'https://packagist.org' + placeholder: 'https://packagist.org', + exposes_secrets: true, + required: false validates :username, presence: true, if: :activated? validates :token, presence: true, if: :activated? diff --git a/app/models/integrations/zentao.rb b/app/models/integrations/zentao.rb index 11db469f7ee..53194089296 100644 --- a/app/models/integrations/zentao.rb +++ b/app/models/integrations/zentao.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Integrations - class Zentao < Integration + class Zentao < BaseIssueTracker include Gitlab::Routing self.field_storage = :data_fields @@ -10,11 +10,13 @@ module Integrations title: -> { s_('ZentaoIntegration|ZenTao Web URL') }, placeholder: 'https://www.zentao.net', help: -> { s_('ZentaoIntegration|Base URL of the ZenTao instance.') }, + exposes_secrets: true, required: true field :api_url, title: -> { s_('ZentaoIntegration|ZenTao API URL (optional)') }, - help: -> { s_('ZentaoIntegration|If different from Web URL.') } + help: -> { s_('ZentaoIntegration|If different from Web URL.') }, + exposes_secrets: true field :api_token, type: 'password', @@ -40,6 +42,17 @@ module Integrations zentao_tracker_data || self.build_zentao_tracker_data end + alias_method :project_url, :url + + def set_default_data + return unless issues_tracker.present? + + return if url + + data_fields.url ||= issues_tracker['url'] + data_fields.api_url ||= issues_tracker['api_url'] + end + def title 'ZenTao' end diff --git a/app/models/project.rb b/app/models/project.rb index 130ab41baa6..127e47695c5 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -2126,6 +2126,7 @@ class Project < ApplicationRecord end def after_import + repository.remove_prohibited_branches repository.expire_content_cache wiki.repository.expire_content_cache diff --git a/app/models/repository.rb b/app/models/repository.rb index 0da71d87457..9039bdf1a20 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1168,6 +1168,16 @@ class Repository @cache ||= Gitlab::RepositoryCache.new(self) end + def remove_prohibited_branches + return unless exists? + + prohibited_branches = raw_repository.branch_names.select { |name| name.match(/\A\h{40}\z/) } + + return if prohibited_branches.blank? + + prohibited_branches.each { |name| raw_repository.delete_branch(name) } + end + private # TODO Genericize finder, later split this on finders by Ref or Oid diff --git a/app/models/snippet.rb b/app/models/snippet.rb index c813c5cb5b8..47b23bbd28a 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -71,8 +71,6 @@ class Snippet < ApplicationRecord }, if: :content_changed? - validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values } - after_create :create_statistics # Scopes diff --git a/app/models/todo.rb b/app/models/todo.rb index cff7a93f72f..c698783d750 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -74,6 +74,7 @@ class Todo < ApplicationRecord scope :for_commit, -> (id) { where(commit_id: id) } scope :with_entity_associations, -> { preload(:target, :author, :note, group: :route, project: [:route, { namespace: [:route, :owner] }]) } scope :joins_issue_and_assignees, -> { left_joins(issue: :assignees) } + scope :for_internal_notes, -> { joins(:note).where(note: { confidential: true }) } enum resolved_by_action: { system_done: 0, api_all_done: 1, api_done: 2, mark_all_done: 3, mark_done: 4 }, _prefix: :resolved_by diff --git a/app/models/u2f_registration.rb b/app/models/u2f_registration.rb index 7c01aa7a420..b4b24ea1486 100644 --- a/app/models/u2f_registration.rb +++ b/app/models/u2f_registration.rb @@ -6,21 +6,7 @@ class U2fRegistration < ApplicationRecord belongs_to :user after_create :create_webauthn_registration - after_update :update_webauthn_registration, if: :counter_changed? - - def create_webauthn_registration - converter = Gitlab::Auth::U2fWebauthnConverter.new(self) - WebauthnRegistration.create!(converter.convert) - rescue StandardError => ex - Gitlab::ErrorTracking.track_exception(ex, u2f_registration_id: self.id) - end - - def update_webauthn_registration - # When we update the sign count of this registration - # we need to update the sign count of the corresponding webauthn registration - # as well if it exists already - WebauthnRegistration.find_by_credential_xid(webauthn_credential_xid)&.update_attribute(:counter, counter) - end + after_update :update_webauthn_registration, if: :saved_change_to_counter? def self.register(user, app_id, params, challenges) u2f = U2F::U2F.new(app_id) @@ -60,10 +46,22 @@ class U2fRegistration < ApplicationRecord private + def create_webauthn_registration + converter = Gitlab::Auth::U2fWebauthnConverter.new(self) + WebauthnRegistration.create!(converter.convert) + rescue StandardError => ex + Gitlab::ErrorTracking.track_exception(ex, u2f_registration_id: self.id) + end + + def update_webauthn_registration + # When we update the sign count of this registration + # we need to update the sign count of the corresponding webauthn registration + # as well if it exists already + WebauthnRegistration.find_by_credential_xid(webauthn_credential_xid) + &.update_attribute(:counter, counter) + end + def webauthn_credential_xid - # To find the corresponding webauthn registration, we use that - # the key handle of the u2f reg corresponds to the credential xid of the webauthn reg - # (with some base64 back and forth) Base64.strict_encode64(Base64.urlsafe_decode64(key_handle)) end end diff --git a/app/models/user.rb b/app/models/user.rb index ca27e0aea6b..1bbad2491d9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -605,23 +605,24 @@ class User < ApplicationRecord end end - # Find a User by their primary email or any associated secondary email + # Find a User by their primary email or any associated confirmed secondary email def find_by_any_email(email, confirmed: false) return unless email by_any_email(email, confirmed: confirmed).take end - # Returns a relation containing all the users for the given email addresses + # Returns a relation containing all found users by their primary email + # or any associated confirmed secondary email # # @param emails [String, Array<String>] email addresses to check - # @param confirmed [Boolean] Only return users where the email is confirmed + # @param confirmed [Boolean] Only return users where the primary email is confirmed def by_any_email(emails, confirmed: false) from_users = by_user_email(emails) from_users = from_users.confirmed if confirmed - from_emails = by_emails(emails) - from_emails = from_emails.confirmed.merge(Email.confirmed) if confirmed + from_emails = by_emails(emails).merge(Email.confirmed) + from_emails = from_emails.confirmed if confirmed items = [from_users, from_emails] @@ -752,6 +753,7 @@ class User < ApplicationRecord matched_by_email_user_id = email_table .project(email_table[:user_id]) .where(email_table[:email].eq(email_address)) + .where(email_table[:confirmed_at].not_eq(nil)) .take(1) # at most 1 record as there is a unique constraint where( @@ -1502,7 +1504,7 @@ class User < ApplicationRecord all_emails = [] all_emails << email unless temp_oauth_email? all_emails << private_commit_email if include_private_email - all_emails.concat(emails.map(&:email)) + all_emails.concat(emails.filter_map { |email| email.email if email.confirmed? }) all_emails.uniq end diff --git a/app/policies/ci/runner_policy.rb b/app/policies/ci/runner_policy.rb index 6dfe9cc496b..8a99f4d1a3e 100644 --- a/app/policies/ci/runner_policy.rb +++ b/app/policies/ci/runner_policy.rb @@ -31,3 +31,5 @@ module Ci rule { ~admin & locked }.prevent :assign_runner end end + +Ci::RunnerPolicy.prepend_mod_with('Ci::RunnerPolicy') diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb index 5f72259f34a..dc7b5e95361 100644 --- a/app/serializers/build_details_entity.rb +++ b/app/serializers/build_details_entity.rb @@ -151,7 +151,7 @@ class BuildDetailsEntity < Ci::JobEntity # We do not return the invalid_dependencies for all scenarios see https://gitlab.com/gitlab-org/gitlab/-/issues/287772#note_914406387 punctuation = invalid_dependencies.empty? ? '.' : ': ' _("This job could not start because it could not retrieve the needed artifacts%{punctuation}%{invalid_dependencies}") % - { invalid_dependencies: invalid_dependencies, punctuation: punctuation } + { invalid_dependencies: html_escape(invalid_dependencies), punctuation: punctuation } end def help_message(docs_url) diff --git a/app/services/concerns/update_visibility_level.rb b/app/services/concerns/update_visibility_level.rb index 4cd14a2fb53..debcff0295c 100644 --- a/app/services/concerns/update_visibility_level.rb +++ b/app/services/concerns/update_visibility_level.rb @@ -5,7 +5,7 @@ module UpdateVisibilityLevel def valid_visibility_level_change?(target, new_visibility) return true unless new_visibility - new_visibility_level = Gitlab::VisibilityLevel.level_value(new_visibility) + new_visibility_level = Gitlab::VisibilityLevel.level_value(new_visibility, fallback_value: nil) if new_visibility_level != target.visibility_level_value unless can?(current_user, :change_visibility_level, target) && diff --git a/app/services/grafana/proxy_service.rb b/app/services/grafana/proxy_service.rb index ac4c3cc091c..37272c85638 100644 --- a/app/services/grafana/proxy_service.rb +++ b/app/services/grafana/proxy_service.rb @@ -15,6 +15,10 @@ module Grafana self.reactive_cache_work_type = :external_dependency self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) } + SUPPORTED_DATASOURCE_PATTERN = %r{\A\d+\z}.freeze + + SUPPORTED_PROXY_PATH = Gitlab::Metrics::Dashboard::Stages::GrafanaFormatter::PROXY_PATH + attr_accessor :project, :datasource_id, :proxy_path, :query_params # @param project_id [Integer] Project id for which grafana is configured. @@ -38,6 +42,7 @@ module Grafana end def execute + return cannot_proxy_response unless can_proxy? return cannot_proxy_response unless client with_reactive_cache(*cache_key) { |result| result } @@ -69,6 +74,11 @@ module Grafana private + def can_proxy? + SUPPORTED_PROXY_PATH == proxy_path && + SUPPORTED_DATASOURCE_PATTERN.match?(datasource_id) + end + def client project.grafana_integration&.client end diff --git a/app/services/groups/destroy_service.rb b/app/services/groups/destroy_service.rb index c88c139a22e..bcf3110ca21 100644 --- a/app/services/groups/destroy_service.rb +++ b/app/services/groups/destroy_service.rb @@ -35,6 +35,8 @@ module Groups user_ids_for_project_authorizations_refresh = obtain_user_ids_for_project_authorizations_refresh + destroy_group_bots + group.destroy if user_ids_for_project_authorizations_refresh.present? @@ -76,6 +78,19 @@ module Groups group.users_ids_of_direct_members end + + # rubocop:disable CodeReuse/ActiveRecord + def destroy_group_bots + bot_ids = group.members_and_requesters.joins(:user).merge(User.project_bot).pluck(:user_id) + current_user_id = current_user.id + + group.run_after_commit do + bot_ids.each do |user_id| + DeleteUserWorker.perform_async(current_user_id, user_id, skip_authorization: true) + end + end + end + # rubocop:enable CodeReuse/ActiveRecord end end diff --git a/app/services/todos/destroy/entity_leave_service.rb b/app/services/todos/destroy/entity_leave_service.rb index 1fe397d24e7..5b04d2fd3af 100644 --- a/app/services/todos/destroy/entity_leave_service.rb +++ b/app/services/todos/destroy/entity_leave_service.rb @@ -41,11 +41,20 @@ module Todos end def remove_confidential_resource_todos + # Deletes todos for confidential issues Todo .for_target(confidential_issues.select(:id)) .for_type(Issue.name) .for_user(user) .delete_all + + # Deletes todos for internal notes on unauthorized projects + Todo + .for_type(Issue.name) + .for_internal_notes + .for_project(non_authorized_reporter_projects) # Only Reporter+ can read internal notes + .for_user(user) + .delete_all end def remove_project_todos diff --git a/app/views/notify/approved_merge_request_email.text.haml b/app/views/notify/approved_merge_request_email.text.haml index 476da7f9af7..ab79a96c4ed 100644 --- a/app/views/notify/approved_merge_request_email.text.haml +++ b/app/views/notify/approved_merge_request_email.text.haml @@ -2,7 +2,7 @@ Merge request #{@merge_request.to_reference} was approved by #{sanitize_name(@ap Merge request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)} -= merge_path_description(@merge_request, 'to') += merge_path_description(@merge_request) Author: #{sanitize_name(@merge_request.author_name)} = assignees_label(@merge_request) diff --git a/app/views/notify/closed_merge_request_email.text.haml b/app/views/notify/closed_merge_request_email.text.haml index 942e771261a..c6e38f5fc3d 100644 --- a/app/views/notify/closed_merge_request_email.text.haml +++ b/app/views/notify/closed_merge_request_email.text.haml @@ -2,7 +2,7 @@ Merge request #{@merge_request.to_reference} was closed by #{sanitize_name(@upda Merge request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)} -= merge_path_description(@merge_request, 'to') += merge_path_description(@merge_request) Author: #{sanitize_name(@merge_request.author_name)} = assignees_label(@merge_request) diff --git a/app/views/notify/merge_request_status_email.text.haml b/app/views/notify/merge_request_status_email.text.haml index f3845b2b910..61c9b130da8 100644 --- a/app/views/notify/merge_request_status_email.text.haml +++ b/app/views/notify/merge_request_status_email.text.haml @@ -2,7 +2,7 @@ = sprintf(s_('Notify|Merge request URL: %{merge_request_url}'), { merge_request_url: project_merge_request_url(@merge_request.target_project, @merge_request) }) -= merge_path_description(@merge_request, 'to') += merge_path_description(@merge_request) = sprintf(s_('Notify|Author: %{author_name}'), { author_name: sanitize_name(@merge_request.author_name) }) = assignees_label(@merge_request) diff --git a/app/views/notify/merge_request_unmergeable_email.html.haml b/app/views/notify/merge_request_unmergeable_email.html.haml index f0a5e5d4367..38c9710f385 100644 --- a/app/views/notify/merge_request_unmergeable_email.html.haml +++ b/app/views/notify/merge_request_unmergeable_email.html.haml @@ -1,7 +1,7 @@ %p = sprintf(s_('Notify|Merge request %{merge_request} can no longer be merged due to conflict.'), { merge_request: merge_request_reference_link(@merge_request) }).html_safe %p - = merge_path_description(@merge_request, 'to') + = merge_path_description(@merge_request) %p = sprintf(s_('Author: %{author_name}'), { author_name: sanitize_name(@merge_request.author_name) }) %p diff --git a/app/views/notify/merge_request_unmergeable_email.text.haml b/app/views/notify/merge_request_unmergeable_email.text.haml index 22d56e73ca8..211e4e379df 100644 --- a/app/views/notify/merge_request_unmergeable_email.text.haml +++ b/app/views/notify/merge_request_unmergeable_email.text.haml @@ -2,7 +2,7 @@ = sprintf(s_('Notify|Merge request URL: %{merge_request_url}'), { merge_request_url: project_merge_request_url(@merge_request.target_project, @merge_request) }) -= merge_path_description(@merge_request, 'to') += merge_path_description(@merge_request) = sprintf(s_('Author: %{author_name}'), { author_name: sanitize_name(@merge_request.author_name) }) = assignees_label(@merge_request) diff --git a/app/views/notify/merge_when_pipeline_succeeds_email.text.haml b/app/views/notify/merge_when_pipeline_succeeds_email.text.haml index 568ca995e04..dbf742a5cbc 100644 --- a/app/views/notify/merge_when_pipeline_succeeds_email.text.haml +++ b/app/views/notify/merge_when_pipeline_succeeds_email.text.haml @@ -2,7 +2,7 @@ Merge request #{@merge_request.to_reference} was scheduled to merge after pipeli Merge request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} -= merge_path_description(@merge_request, 'to') += merge_path_description(@merge_request) Author: #{sanitize_name(@merge_request.author_name)} = assignees_label(@merge_request) diff --git a/app/views/notify/merged_merge_request_email.html.haml b/app/views/notify/merged_merge_request_email.html.haml index e2e4d6d937f..bf50ad0a9ad 100644 --- a/app/views/notify/merged_merge_request_email.html.haml +++ b/app/views/notify/merged_merge_request_email.html.haml @@ -2,7 +2,7 @@ = sprintf(s_('Notify|Merge request %{merge_request} was merged'), { merge_request: merge_request_reference_link(@merge_request) }).html_safe %p - = merge_path_description(@merge_request, 'to') + = merge_path_description(@merge_request) %div = sprintf(s_('Notify|Author: %{author_name}'), { author_name: sanitize_name(@merge_request.author_name) }) diff --git a/app/views/notify/merged_merge_request_email.text.haml b/app/views/notify/merged_merge_request_email.text.haml index 9b9eb566903..b80e4606f35 100644 --- a/app/views/notify/merged_merge_request_email.text.haml +++ b/app/views/notify/merged_merge_request_email.text.haml @@ -2,7 +2,7 @@ = sprintf(s_('Notify|Merge request URL: %{merge_request_url}'), { merge_request_url: project_merge_request_url(@merge_request.target_project, @merge_request) }) -= merge_path_description(@merge_request, 'to') += merge_path_description(@merge_request) = sprintf(s_('Notify|Author: %{author_name}'), { author_name: sanitize_name(@merge_request.author_name) }) diff --git a/app/views/notify/new_mention_in_merge_request_email.text.erb b/app/views/notify/new_mention_in_merge_request_email.text.erb index 9ba86f17ef6..9a45aaf1148 100644 --- a/app/views/notify/new_mention_in_merge_request_email.text.erb +++ b/app/views/notify/new_mention_in_merge_request_email.text.erb @@ -2,7 +2,7 @@ You have been mentioned in merge request <%= @merge_request.to_reference %> <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> -<%= merge_path_description(@merge_request, 'to') %> +<%= merge_path_description(@merge_request) %> Author: <%= sanitize_name(@merge_request.author_name) %> <%= assignees_label(@merge_request) %> <%= reviewers_label(@merge_request) %> diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml index 1542d5bba85..3c60235e6d2 100644 --- a/app/views/notify/new_merge_request_email.html.haml +++ b/app/views/notify/new_merge_request_email.html.haml @@ -3,7 +3,7 @@ mr_link: merge_request_reference_link(@merge_request) } .branch - = merge_path_description(@merge_request, 'to') + = merge_path_description(@merge_request) .author Author: #{@merge_request.author_name} .assignee diff --git a/app/views/notify/new_merge_request_email.text.erb b/app/views/notify/new_merge_request_email.text.erb index 09e8ca36225..f2be0b71592 100644 --- a/app/views/notify/new_merge_request_email.text.erb +++ b/app/views/notify/new_merge_request_email.text.erb @@ -2,7 +2,7 @@ mr_link: url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) } %> -<%= merge_path_description(@merge_request, 'to') %> +<%= merge_path_description(@merge_request) %> <%= "#{_('Author')}: #{sanitize_name(@merge_request.author_name)}" %> <%= assignees_label(@merge_request) if @merge_request.assignees.any? %> <%= reviewers_label(@merge_request) if @merge_request.reviewers.any? %> diff --git a/app/views/notify/unapproved_merge_request_email.text.haml b/app/views/notify/unapproved_merge_request_email.text.haml index 4e34b883906..52c65e6f5c6 100644 --- a/app/views/notify/unapproved_merge_request_email.text.haml +++ b/app/views/notify/unapproved_merge_request_email.text.haml @@ -2,7 +2,7 @@ Merge request #{@merge_request.to_reference} was unapproved by #{@unapproved_by. Merge request URL: #{project_merge_request_url(@merge_request.target_project, @merge_request)} -= merge_path_description(@merge_request, 'to') += merge_path_description(@merge_request) Author: #{sanitize_name(@merge_request.author_name)} = assignees_label(@merge_request) |