Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-07-28 21:09:03 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-07-28 21:09:03 +0300
commitb420660ef1369fec4d09b7bf5e961d81980974e5 (patch)
treef096fdd1bd3b4898e1b2ca80957ce68c200c09f0 /app
parentb8026fd558e7ec154c626208a33c1485aec8f4ea (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_tabs.vue18
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_reports.vue20
-rw-r--r--app/assets/javascripts/pipelines/pipeline_tabs.js25
-rw-r--r--app/controllers/autocomplete_controller.rb5
-rw-r--r--app/finders/issuable_finder.rb20
-rw-r--r--app/graphql/gitlab_schema.rb2
-rw-r--r--app/graphql/types/global_id_type.rb2
-rw-r--r--app/helpers/merge_requests_helper.rb26
-rw-r--r--app/helpers/projects/pipeline_helper.rb9
-rw-r--r--app/models/error_tracking/project_error_tracking_setting.rb8
-rw-r--r--app/models/grafana_integration.rb8
-rw-r--r--app/models/hooks/web_hook_log.rb7
-rw-r--r--app/models/integrations/campfire.rb9
-rw-r--r--app/models/integrations/drone_ci.rb1
-rw-r--r--app/models/integrations/jira.rb8
-rw-r--r--app/models/integrations/packagist.rb10
-rw-r--r--app/models/integrations/zentao.rb17
-rw-r--r--app/models/project.rb1
-rw-r--r--app/models/repository.rb10
-rw-r--r--app/models/snippet.rb2
-rw-r--r--app/models/todo.rb1
-rw-r--r--app/models/u2f_registration.rb34
-rw-r--r--app/models/user.rb14
-rw-r--r--app/policies/ci/runner_policy.rb2
-rw-r--r--app/serializers/build_details_entity.rb2
-rw-r--r--app/services/concerns/update_visibility_level.rb2
-rw-r--r--app/services/grafana/proxy_service.rb10
-rw-r--r--app/services/groups/destroy_service.rb15
-rw-r--r--app/services/todos/destroy/entity_leave_service.rb9
-rw-r--r--app/views/notify/approved_merge_request_email.text.haml2
-rw-r--r--app/views/notify/closed_merge_request_email.text.haml2
-rw-r--r--app/views/notify/merge_request_status_email.text.haml2
-rw-r--r--app/views/notify/merge_request_unmergeable_email.html.haml2
-rw-r--r--app/views/notify/merge_request_unmergeable_email.text.haml2
-rw-r--r--app/views/notify/merge_when_pipeline_succeeds_email.text.haml2
-rw-r--r--app/views/notify/merged_merge_request_email.html.haml2
-rw-r--r--app/views/notify/merged_merge_request_email.text.haml2
-rw-r--r--app/views/notify/new_mention_in_merge_request_email.text.erb2
-rw-r--r--app/views/notify/new_merge_request_email.html.haml2
-rw-r--r--app/views/notify/new_merge_request_email.text.erb2
-rw-r--r--app/views/notify/unapproved_merge_request_email.text.haml2
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)