diff --git a/.rubocop.yml b/.rubocop.yml
index f017e0758fd..2a49c385a84 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -16,6 +16,9 @@ inherit_from:
<% if RUBY_VERSION[/^\d+\.\d+/, 0] == '3.1' %>
- ./rubocop/rubocop-ruby31.yml
<% end %>
+ <% if RUBY_VERSION[/^\d+\.\d+/, 0] == '3.2' %>
+ - ./rubocop/rubocop-ruby32.yml
+ <% end %>
- ./rubocop/rubocop-migrations.yml
- ./rubocop/rubocop-usage-data.yml
- ./rubocop/rubocop-code_reuse.yml
diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml
index a786a2ad510..589327e15a1 100644
--- a/.rubocop_todo/layout/line_length.yml
+++ b/.rubocop_todo/layout/line_length.yml
@@ -340,7 +340,6 @@ Layout/LineLength:
- 'app/models/incident_management/project_incident_management_setting.rb'
- 'app/models/instance_configuration.rb'
- 'app/models/integrations/asana.rb'
- - 'app/models/integrations/bamboo.rb'
- 'app/models/integrations/base_chat_notification.rb'
- 'app/models/integrations/base_issue_tracker.rb'
- 'app/models/integrations/bugzilla.rb'
@@ -4199,7 +4198,6 @@ Layout/LineLength:
- 'spec/models/import_failure_spec.rb'
- 'spec/models/instance_configuration_spec.rb'
- 'spec/models/integration_spec.rb'
- - 'spec/models/integrations/bamboo_spec.rb'
- 'spec/models/integrations/base_chat_notification_spec.rb'
- 'spec/models/integrations/base_issue_tracker_spec.rb'
- 'spec/models/integrations/chat_message/base_message_spec.rb'
diff --git a/.rubocop_todo/lint/redundant_require_statement.yml b/.rubocop_todo/lint/redundant_require_statement.yml
deleted file mode 100644
index 28c91fbded1..00000000000
--- a/.rubocop_todo/lint/redundant_require_statement.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-# Cop supports --autocorrect.
-Lint/RedundantRequireStatement:
- Enabled: false
diff --git a/.rubocop_todo/lint/unused_method_argument.yml b/.rubocop_todo/lint/unused_method_argument.yml
index 6c4d0d12ca9..9f20adf1d9e 100644
--- a/.rubocop_todo/lint/unused_method_argument.yml
+++ b/.rubocop_todo/lint/unused_method_argument.yml
@@ -118,7 +118,6 @@ Lint/UnusedMethodArgument:
- 'app/models/discussion.rb'
- 'app/models/external_issue.rb'
- 'app/models/group.rb'
- - 'app/models/integrations/bamboo.rb'
- 'app/models/integrations/buildkite.rb'
- 'app/models/integrations/discord.rb'
- 'app/models/integrations/ewm.rb'
diff --git a/.rubocop_todo/rspec/context_wording.yml b/.rubocop_todo/rspec/context_wording.yml
index e76e33b35a3..b3d65fd2f33 100644
--- a/.rubocop_todo/rspec/context_wording.yml
+++ b/.rubocop_todo/rspec/context_wording.yml
@@ -2177,7 +2177,6 @@ RSpec/ContextWording:
- 'spec/models/import_export_upload_spec.rb'
- 'spec/models/import_failure_spec.rb'
- 'spec/models/integration_spec.rb'
- - 'spec/models/integrations/bamboo_spec.rb'
- 'spec/models/integrations/base_chat_notification_spec.rb'
- 'spec/models/integrations/chat_message/issue_message_spec.rb'
- 'spec/models/integrations/chat_message/merge_message_spec.rb'
diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml
index 39d1c8eb393..28a193130bb 100644
--- a/.rubocop_todo/rspec/missing_feature_category.yml
+++ b/.rubocop_todo/rspec/missing_feature_category.yml
@@ -4745,7 +4745,6 @@ RSpec/MissingFeatureCategory:
- 'spec/models/instance_metadata_spec.rb'
- 'spec/models/integrations/asana_spec.rb'
- 'spec/models/integrations/assembla_spec.rb'
- - 'spec/models/integrations/bamboo_spec.rb'
- 'spec/models/integrations/base_issue_tracker_spec.rb'
- 'spec/models/integrations/base_slack_notification_spec.rb'
- 'spec/models/integrations/base_third_party_wiki_spec.rb'
diff --git a/.rubocop_todo/style/format_string.yml b/.rubocop_todo/style/format_string.yml
index 0a55967106f..885687f36b1 100644
--- a/.rubocop_todo/style/format_string.yml
+++ b/.rubocop_todo/style/format_string.yml
@@ -78,7 +78,6 @@ Style/FormatString:
- 'app/models/diff_note.rb'
- 'app/models/diff_viewer/base.rb'
- 'app/models/integrations/asana.rb'
- - 'app/models/integrations/bamboo.rb'
- 'app/models/integrations/bugzilla.rb'
- 'app/models/integrations/chat_message/pipeline_message.rb'
- 'app/models/integrations/confluence.rb'
@@ -301,7 +300,6 @@ Style/FormatString:
- 'spec/lib/gitlab/config_checker/external_database_checker_spec.rb'
- 'spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb'
- 'spec/lib/gitlab/usage/service_ping_report_spec.rb'
- - 'spec/models/integrations/bamboo_spec.rb'
- 'spec/models/integrations/datadog_spec.rb'
- 'spec/requests/api/graphql/project/jira_projects_spec.rb'
- 'spec/services/groups/import_export/export_service_spec.rb'
diff --git a/.rubocop_todo/style/hash_syntax.yml b/.rubocop_todo/style/hash_syntax.yml
deleted file mode 100644
index a6fb5b2e59c..00000000000
--- a/.rubocop_todo/style/hash_syntax.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-# Cop supports --autocorrect.
-Style/HashSyntax:
- Enabled: false
diff --git a/.rubocop_todo/style/percent_literal_delimiters.yml b/.rubocop_todo/style/percent_literal_delimiters.yml
index ab096ed9a5a..28717857578 100644
--- a/.rubocop_todo/style/percent_literal_delimiters.yml
+++ b/.rubocop_todo/style/percent_literal_delimiters.yml
@@ -320,7 +320,6 @@ Style/PercentLiteralDelimiters:
- 'spec/models/group_spec.rb'
- 'spec/models/instance_configuration_spec.rb'
- 'spec/models/integration_spec.rb'
- - 'spec/models/integrations/bamboo_spec.rb'
- 'spec/models/integrations/buildkite_spec.rb'
- 'spec/models/integrations/campfire_spec.rb'
- 'spec/models/integrations/jira_spec.rb'
diff --git a/.rubocop_todo/style/single_argument_dig.yml b/.rubocop_todo/style/single_argument_dig.yml
index d41dc0add05..b33a9954cfc 100644
--- a/.rubocop_todo/style/single_argument_dig.yml
+++ b/.rubocop_todo/style/single_argument_dig.yml
@@ -6,7 +6,6 @@ Style/SingleArgumentDig:
- 'app/models/ci/build.rb'
- 'app/models/ci/build_report_result.rb'
- 'app/models/error_tracking/error_event.rb'
- - 'app/models/integrations/bamboo.rb'
- 'app/serializers/codequality_degradation_entity.rb'
- 'app/services/ci/update_build_state_service.rb'
- 'ee/app/controllers/subscriptions_controller.rb'
diff --git a/app/assets/javascripts/admin/abuse_report/components/report_actions.vue b/app/assets/javascripts/admin/abuse_report/components/report_actions.vue
index 560d733c10c..e005e183c9f 100644
--- a/app/assets/javascripts/admin/abuse_report/components/report_actions.vue
+++ b/app/assets/javascripts/admin/abuse_report/components/report_actions.vue
@@ -14,8 +14,10 @@ import { DRAWER_Z_INDEX } from '~/lib/utils/constants';
import {
ACTIONS_I18N,
NO_ACTION,
+ TRUST_ACTION,
USER_ACTION_OPTIONS,
REASON_OPTIONS,
+ TRUST_REASON,
STATUS_OPEN,
SUCCESS_ALERT,
FAILED_ALERT,
@@ -77,6 +79,16 @@ export default {
userActionOptions() {
return this.isNotCurrentUser ? USER_ACTION_OPTIONS : [NO_ACTION];
},
+ reasonOptions() {
+ if (!this.isNotCurrentUser) {
+ return [];
+ }
+
+ if (this.form.user_action === TRUST_ACTION.value) {
+ return [TRUST_REASON];
+ }
+ return REASON_OPTIONS;
+ },
},
methods: {
toggleActionsDrawer() {
@@ -120,7 +132,6 @@ export default {
},
},
i18n: ACTIONS_I18N,
- reasonOptions: REASON_OPTIONS,
DRAWER_Z_INDEX,
};
@@ -173,7 +184,7 @@ export default {
id="reason"
v-model="form.reason"
data-testid="reason-select"
- :options="$options.reasonOptions"
+ :options="reasonOptions"
:state="validationState.reason"
@change="validateReason"
/>
diff --git a/app/assets/javascripts/admin/abuse_report/constants.js b/app/assets/javascripts/admin/abuse_report/constants.js
index 1ecef44ab8f..94ef911e853 100644
--- a/app/assets/javascripts/admin/abuse_report/constants.js
+++ b/app/assets/javascripts/admin/abuse_report/constants.js
@@ -25,11 +25,14 @@ export const ACTIONS_I18N = {
};
export const NO_ACTION = { value: '', text: s__('AbuseReport|No action') };
+export const TRUST_REASON = { value: 'trusted', text: s__(`AbuseReport|Confirmed trusted user`) };
+export const TRUST_ACTION = { value: 'trust_user', text: s__('AbuseReport|Trust user') };
export const USER_ACTION_OPTIONS = [
NO_ACTION,
{ value: 'block_user', text: s__('AbuseReport|Block user') },
{ value: 'ban_user', text: s__('AbuseReport|Ban user') },
+ TRUST_ACTION,
{ value: 'delete_user', text: s__('AbuseReport|Delete user') },
];
diff --git a/app/assets/javascripts/graphql_shared/possible_types.json b/app/assets/javascripts/graphql_shared/possible_types.json
index 37c1674cc5a..4e0b1413f71 100644
--- a/app/assets/javascripts/graphql_shared/possible_types.json
+++ b/app/assets/javascripts/graphql_shared/possible_types.json
@@ -3,6 +3,9 @@
"AlertManagementHttpIntegration",
"AlertManagementPrometheusIntegration"
],
+ "AmazonS3ConfigurationInterface": [
+ "AmazonS3ConfigurationType"
+ ],
"BaseHeaderInterface": [
"AuditEventStreamingHeader",
"AuditEventsStreamingInstanceHeader"
diff --git a/app/helpers/resource_events/abuse_report_events_helper.rb b/app/helpers/resource_events/abuse_report_events_helper.rb
index 8adbc891184..207ec73454b 100644
--- a/app/helpers/resource_events/abuse_report_events_helper.rb
+++ b/app/helpers/resource_events/abuse_report_events_helper.rb
@@ -10,6 +10,8 @@ module ResourceEvents
s_('AbuseReportEvent|Successfully blocked the user')
when 'delete_user'
s_('AbuseReportEvent|Successfully scheduled the user for deletion')
+ when 'trust_user'
+ s_('AbuseReportEvent|Successfully trusted the user')
when 'close_report'
s_('AbuseReportEvent|Successfully closed the report')
when 'ban_user_and_close_report'
@@ -18,6 +20,8 @@ module ResourceEvents
s_('AbuseReportEvent|Successfully blocked the user and closed the report')
when 'delete_user_and_close_report'
s_('AbuseReportEvent|Successfully scheduled the user for deletion and closed the report')
+ when 'trust_user_and_close_report'
+ s_('AbuseReportEvent|Successfully trusted the user and closed the report')
end
end
end
diff --git a/app/models/integrations/bamboo.rb b/app/models/integrations/bamboo.rb
index 4b98014e0cc..9f15532a0b0 100644
--- a/app/models/integrations/bamboo.rb
+++ b/app/models/integrations/bamboo.rb
@@ -28,14 +28,13 @@ module Integrations
non_empty_password_title: -> { s_('ProjectService|Enter new password') },
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current password') }
- validates :bamboo_url, presence: true, public_url: true, if: :activated?
- validates :build_key, presence: true, if: :activated?
- validates :username,
- presence: true,
- if: ->(service) { service.activated? && service.password }
- validates :password,
- presence: true,
- if: ->(service) { service.activated? && service.username }
+ with_options if: :activated? do
+ validates :bamboo_url, presence: true, public_url: true
+ validates :build_key, presence: true
+ end
+
+ validates :username, presence: true, if: ->(integration) { integration.activated? && integration.password }
+ validates :password, presence: true, if: ->(integration) { integration.activated? && integration.username }
attr_accessor :response
@@ -48,8 +47,16 @@ module Integrations
end
def help
- docs_link = ActionController::Base.helpers.link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/bamboo'), target: '_blank', rel: 'noopener noreferrer'
- s_('BambooService|Run CI/CD pipelines with Atlassian Bamboo. You must set up automatic revision labeling and a repository trigger in Bamboo. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
+ docs_link = ActionController::Base.helpers.link_to(
+ _('Learn more.'),
+ Rails.application.routes.url_helpers.help_page_url('user/project/integrations/bamboo'),
+ target: '_blank',
+ rel: 'noopener noreferrer'
+ )
+ format(
+ s_('BambooService|Run CI/CD pipelines with Atlassian Bamboo. You must set up automatic revision labeling and ' \
+ 'a repository trigger in Bamboo. %{docs_link}').html_safe,
+ docs_link: docs_link.html_safe)
end
def self.to_param
@@ -70,14 +77,16 @@ module Integrations
get_path("updateAndBuild.action", { buildKey: build_key })
end
- def calculate_reactive_cache(sha, ref)
+ def calculate_reactive_cache(sha, _ref)
response = try_get_path("rest/api/latest/result/byChangeset/#{sha}")
{ build_page: read_build_page(response), commit_status: read_commit_status(response) }
end
def avatar_url
- ActionController::Base.helpers.image_path('illustrations/third-party-logos/integrations-logos/atlassian-bamboo.svg')
+ ActionController::Base.helpers.image_path(
+ 'illustrations/third-party-logos/integrations-logos/atlassian-bamboo.svg'
+ )
end
private
@@ -116,7 +125,7 @@ module Integrations
if result.blank?
'Pending'
else
- result.dig('buildState')
+ result['buildState']
end
return :error unless status.present?
diff --git a/app/models/resource_events/abuse_report_event.rb b/app/models/resource_events/abuse_report_event.rb
index 59f88a63998..5881f87241d 100644
--- a/app/models/resource_events/abuse_report_event.rb
+++ b/app/models/resource_events/abuse_report_event.rb
@@ -16,7 +16,9 @@ module ResourceEvents
close_report: 4,
ban_user_and_close_report: 5,
block_user_and_close_report: 6,
- delete_user_and_close_report: 7
+ delete_user_and_close_report: 7,
+ trust_user: 8,
+ trust_user_and_close_report: 9
}
enum reason: {
@@ -28,7 +30,8 @@ module ResourceEvents
copyright: 6,
malware: 7,
other: 8,
- unconfirmed: 9
+ unconfirmed: 9,
+ trusted: 10
}
def success_message
diff --git a/app/models/user.rb b/app/models/user.rb
index e17803af135..b76d19240f8 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -271,6 +271,7 @@ class User < MainClusterwide::ApplicationRecord
has_many :bulk_imports
has_many :custom_attributes, class_name: 'UserCustomAttribute'
+ has_one :trusted_with_spam_attribute, -> { UserCustomAttribute.trusted_with_spam }, class_name: 'UserCustomAttribute'
has_many :callouts, class_name: 'Users::Callout'
has_many :group_callouts, class_name: 'Users::GroupCallout'
has_many :project_callouts, class_name: 'Users::ProjectCallout'
@@ -2223,8 +2224,8 @@ class User < MainClusterwide::ApplicationRecord
}
end
- def allow_possible_spam?
- custom_attributes.by_key(UserCustomAttribute::ALLOW_POSSIBLE_SPAM).exists?
+ def trusted?
+ trusted_with_spam_attribute.present?
end
def namespace_commit_email_for_namespace(namespace)
diff --git a/app/models/user_custom_attribute.rb b/app/models/user_custom_attribute.rb
index 15d50071bf6..b2674cb4e88 100644
--- a/app/models/user_custom_attribute.rb
+++ b/app/models/user_custom_attribute.rb
@@ -10,13 +10,14 @@ class UserCustomAttribute < ApplicationRecord
scope :by_user_id, ->(user_id) { where(user_id: user_id) }
scope :by_updated_at, ->(updated_at) { where(updated_at: updated_at) }
scope :arkose_sessions, -> { by_key('arkose_session') }
+ scope :trusted_with_spam, -> { by_key(TRUSTED_BY) }
BLOCKED_BY = 'blocked_by'
UNBLOCKED_BY = 'unblocked_by'
ARKOSE_RISK_BAND = 'arkose_risk_band'
AUTO_BANNED_BY_ABUSE_REPORT_ID = 'auto_banned_by_abuse_report_id'
AUTO_BANNED_BY_SPAM_LOG_ID = 'auto_banned_by_spam_log_id'
- ALLOW_POSSIBLE_SPAM = 'allow_possible_spam'
+ TRUSTED_BY = 'trusted_by'
IDENTITY_VERIFICATION_PHONE_EXEMPT = 'identity_verification_phone_exempt'
class << self
@@ -50,6 +51,17 @@ class UserCustomAttribute < ApplicationRecord
return unless spam_log
custom_attribute = { user_id: spam_log.user_id, key: AUTO_BANNED_BY_SPAM_LOG_ID, value: spam_log.id }
+ upsert_custom_attributes([custom_attribute])
+ end
+
+ def set_trusted_by(user:, trusted_by:)
+ return unless user && trusted_by
+
+ custom_attribute = {
+ user_id: user.id,
+ key: UserCustomAttribute::TRUSTED_BY,
+ value: "#{trusted_by.username}/#{trusted_by.id}+#{Time.current}"
+ }
upsert_custom_attributes([custom_attribute])
end
diff --git a/app/services/admin/abuse_reports/moderate_user_service.rb b/app/services/admin/abuse_reports/moderate_user_service.rb
index 823568d9db8..1e14806c694 100644
--- a/app/services/admin/abuse_reports/moderate_user_service.rb
+++ b/app/services/admin/abuse_reports/moderate_user_service.rb
@@ -42,6 +42,7 @@ module Admin
when :block_user then block_user
when :delete_user then delete_user
when :close_report then close_report
+ when :trust_user then trust_user
end
end
@@ -66,6 +67,10 @@ module Admin
success
end
+ def trust_user
+ Users::TrustService.new(current_user).execute(abuse_report.user)
+ end
+
def close_similar_open_reports
# admins see the abuse report and other open reports for the same user in one page
# hence, if the request is to close the report, close other open reports for the same user too
diff --git a/app/services/spam/spam_verdict_service.rb b/app/services/spam/spam_verdict_service.rb
index 9efe51b43b8..2d4bebc8b2b 100644
--- a/app/services/spam/spam_verdict_service.rb
+++ b/app/services/spam/spam_verdict_service.rb
@@ -90,7 +90,7 @@ module Spam
end
def allow_possible_spam?
- target.allow_possible_spam?(user) || user.allow_possible_spam?
+ target.allow_possible_spam?(user) || user.trusted?
end
def spamcheck_client
diff --git a/app/services/users/allow_possible_spam_service.rb b/app/services/users/allow_possible_spam_service.rb
deleted file mode 100644
index d9273fe0fc1..00000000000
--- a/app/services/users/allow_possible_spam_service.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-module Users
- class AllowPossibleSpamService < BaseService
- def initialize(current_user)
- @current_user = current_user
- end
-
- def execute(user)
- custom_attribute = {
- user_id: user.id,
- key: UserCustomAttribute::ALLOW_POSSIBLE_SPAM,
- value: "#{current_user.username}/#{current_user.id}+#{Time.current}"
- }
- UserCustomAttribute.upsert_custom_attributes([custom_attribute])
- end
- end
-end
diff --git a/app/services/users/disallow_possible_spam_service.rb b/app/services/users/trust_service.rb
similarity index 53%
rename from app/services/users/disallow_possible_spam_service.rb
rename to app/services/users/trust_service.rb
index e31ba7ddff0..faf0b9c40ea 100644
--- a/app/services/users/disallow_possible_spam_service.rb
+++ b/app/services/users/trust_service.rb
@@ -1,13 +1,14 @@
# frozen_string_literal: true
module Users
- class DisallowPossibleSpamService < BaseService
+ class TrustService < BaseService
def initialize(current_user)
@current_user = current_user
end
def execute(user)
- user.custom_attributes.by_key(UserCustomAttribute::ALLOW_POSSIBLE_SPAM).delete_all
+ UserCustomAttribute.set_trusted_by(user: user, trusted_by: @current_user)
+ success
end
end
end
diff --git a/app/services/users/untrust_service.rb b/app/services/users/untrust_service.rb
new file mode 100644
index 00000000000..aa5de71b97f
--- /dev/null
+++ b/app/services/users/untrust_service.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Users
+ class UntrustService < BaseService
+ def initialize(current_user)
+ @current_user = current_user
+ end
+
+ def execute(user)
+ user.trusted_with_spam_attribute.delete
+ success
+ end
+ end
+end
diff --git a/app/views/admin/application_settings/_localization.html.haml b/app/views/admin/application_settings/_localization.html.haml
index 4002aa076f7..25038e6f221 100644
--- a/app/views/admin/application_settings/_localization.html.haml
+++ b/app/views/admin/application_settings/_localization.html.haml
@@ -7,7 +7,7 @@
= f.select :first_day_of_week, first_day_of_week_choices, {}, class: 'form-control'
.form-text.text-muted
= _('Default first day of the week in calendars and date pickers.')
- = link_to _('Learn more.'), help_page_path('administration/settings/index.md', anchor: 'change-the-default-first-day-of-the-week'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('administration/settings/localization.md', anchor: 'change-the-default-first-day-of-the-week'), target: '_blank', rel: 'noopener noreferrer'
.form-group
= f.label :time_tracking, _('Time tracking'), class: 'label-bold'
diff --git a/doc/administration/audit_event_streaming/audit_event_types.md b/doc/administration/audit_event_streaming/audit_event_types.md
index a91493c79be..7fea381fd09 100644
--- a/doc/administration/audit_event_streaming/audit_event_types.md
+++ b/doc/administration/audit_event_streaming/audit_event_types.md
@@ -37,6 +37,7 @@ audit events to external destinations.
| [`allow_committer_approval_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102256) | Event triggered on updating prevent merge request approval from committers from group merge request setting | **{check-circle}** Yes | **{check-circle}** Yes | `compliance_management` | GitLab [15.6](https://gitlab.com/gitlab-org/gitlab/-/issues/373949) |
| [`allow_merge_on_skipped_pipeline_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83922) | There is a project setting which toggles the ability to merge when a pipeline is skipped. This audit event tracks changes to that setting. This MR adds a setting to allow this (like previous GitLab versions). | **{check-circle}** Yes | **{check-circle}** Yes | `continuous_integration` | GitLab [14.10](https://gitlab.com/gitlab-org/gitlab/-/issues/301124) |
| [`allow_overrides_to_approver_list_per_merge_request_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/102256) | Event triggered on updating prevent users from modifying MR approval rules in merge requests from group merge request setting | **{check-circle}** Yes | **{check-circle}** Yes | `compliance_management` | GitLab [15.6](https://gitlab.com/gitlab-org/gitlab/-/issues/373949) |
+| [`amazon_s3_configuration_created`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132443) | Triggered when Amazon S3 configuration for audit events streaming is created | **{check-circle}** Yes | **{check-circle}** Yes | `audit_events` | GitLab [16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/423229) |
| [`application_setting_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/124639) | Triggered when Application setting is updated | **{check-circle}** Yes | **{check-circle}** Yes | `system_access` | GitLab [16.3](https://gitlab.com/gitlab-org/gitlab/-/issues/282428) |
| [`approval_rule_created`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89939) | Triggered when a merge request approval rule is created | **{check-circle}** Yes | **{check-circle}** Yes | `source_code_management` | GitLab [15.2](https://gitlab.com/gitlab-org/gitlab/-/issues/363092) |
| [`approval_rule_deleted`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82297) | Triggered on successful approval rule deletion | **{check-circle}** Yes | **{check-circle}** Yes | `source_code_management` | GitLab [14.9](https://gitlab.com/gitlab-org/gitlab/-/issues/329514) |
diff --git a/doc/administration/review_abuse_reports.md b/doc/administration/review_abuse_reports.md
index 4ff53a4e1b0..84bb7ab219f 100644
--- a/doc/administration/review_abuse_reports.md
+++ b/doc/administration/review_abuse_reports.md
@@ -32,13 +32,15 @@ To find out more about reporting abuse, see
## Resolving abuse reports
+> **Trust user** [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131102) in GitLab 16.4.
+
To access abuse reports:
1. On the left sidebar, select **Search or go to**.
1. Select **Admin Area**.
1. Select **Abuse Reports**.
-There are 3 ways to resolve an abuse report, with a button for each method:
+There are four ways to resolve an abuse report, with a button for each method:
- Remove user & report. This:
- [Deletes the reported user](../user/profile/account/delete_account.md) from the
@@ -48,6 +50,9 @@ There are 3 ways to resolve an abuse report, with a button for each method:
- Remove report. This:
- Removes the abuse report from the list.
- Removes access restrictions for the reported user.
+- Trust user. This:
+ - Allows the user to create issues, notes, snippets, and merge requests without being blocked for spam.
+ - Prevents abuse reports from being created for this user.
The following is an example of the **Abuse Reports** page:
diff --git a/doc/administration/settings/index.md b/doc/administration/settings/index.md
index 77bac42d899..1c601df7814 100644
--- a/doc/administration/settings/index.md
+++ b/doc/administration/settings/index.md
@@ -1,18 +1,16 @@
---
-stage: Create
-group: Source Code
+stage: none
+group: unassigned
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
type: index
---
-# Admin Area settings **(FREE SELF)**
+# Update your Admin Area settings **(FREE SELF)**
As an administrator of a GitLab self-managed instance, you can manage the behavior of your
deployment.
-The **Admin Area** is not accessible on GitLab.com, and settings can only be changed by the
-GitLab.com administrators. For the settings and limits on the GitLab.com instance,
-read [GitLab.com settings](../../user/gitlab_com/index.md).
+Use **Settings** to control settings across the instance.
## Access the Admin Area
@@ -21,23 +19,3 @@ To access the **Admin Area**:
1. Sign in to your GitLab instance as an administrator.
1. On the left sidebar, select **Search or go to**.
1. Select **Admin Area**.
-
-## Change the default first day of the week
-
-You can change the [Default first day of the week](../../user/profile/preferences.md)
-for the entire GitLab instance:
-
-1. On the left sidebar, select **Search or go to**.
-1. Select **Admin Area**.
-1. Select **Settings > Preferences**.
-1. Scroll to the **Localization** section, and select your desired first day of the week.
-
-## Change the default language
-
-You can change the [Default language](../../user/profile/preferences.md)
-for the entire GitLab instance:
-
-1. On the left sidebar, select **Search or go to**.
-1. Select **Admin Area**.
-1. Select **Settings > Preferences**.
-1. Scroll to the **Localization** section, and select your desired default language.
diff --git a/doc/administration/settings/localization.md b/doc/administration/settings/localization.md
new file mode 100644
index 00000000000..a86e9c75a4e
--- /dev/null
+++ b/doc/administration/settings/localization.md
@@ -0,0 +1,31 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+type: index
+---
+
+# Localization **(FREE SELF)**
+
+As an administrator of a GitLab self-managed instance, you can manage the behavior of your
+deployment.
+
+## Change the default first day of the week
+
+You can change the [Default first day of the week](../../user/profile/preferences.md)
+for the entire GitLab instance:
+
+1. On the left sidebar, select **Search or go to**.
+1. Select **Admin Area**.
+1. Select **Settings > Preferences**.
+1. Scroll to the **Localization** section, and select your desired first day of the week.
+
+## Change the default language
+
+You can change the [Default language](../../user/profile/preferences.md)
+for the entire GitLab instance:
+
+1. On the left sidebar, select **Search or go to**.
+1. Select **Admin Area**.
+1. Select **Settings > Preferences**.
+1. Scroll to the **Localization** section, and select your desired default language.
diff --git a/doc/administration/settings/rate_limits.md b/doc/administration/settings/rate_limits.md
new file mode 100644
index 00000000000..2263455fa09
--- /dev/null
+++ b/doc/administration/settings/rate_limits.md
@@ -0,0 +1,24 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+type: index
+---
+
+# Rate limits
+
+You can change network settings to limit the rate of connections with your instance.
+
+- [Deprecated API rate limits](deprecated_api_rate_limits.md)
+- [Git LFS](git_lfs_rate_limits.md)
+- [Git SSH operations](rate_limits_on_git_ssh_operations.md)
+- [Incident management](incident_management_rate_limits.md)
+- [Issue creation](rate_limit_on_issues_creation.md)
+- [Note creation](rate_limit_on_notes_creation.md)
+- [Package Registry](package_registry_rate_limits.md)
+- [Pipeline creation](rate_limit_on_pipelines_creation.md)
+- [Projects API](rate_limit_on_projects_api.md)
+- [Raw endpoints](rate_limits_on_raw_endpoints.md)
+- [Repository files API](files_api_rate_limits.md)
+- [User and IP](user_and_ip_rate_limits.md)
+- [Users API](rate_limit_on_users_api.md)
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index b4f57847408..d0965706696 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1274,6 +1274,30 @@ Input type: `AlertTodoCreateInput`
| `issue` | [`Issue`](#issue) | Issue created after mutation. |
| `todo` | [`Todo`](#todo) | To-do item after mutation. |
+### `Mutation.amazonS3ConfigurationCreate`
+
+Input type: `AmazonS3ConfigurationCreateInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `accessKeyXid` | [`String!`](#string) | Access key ID of the Amazon S3 account. |
+| `awsRegion` | [`String!`](#string) | AWS region where the bucket is created. |
+| `bucketName` | [`String!`](#string) | Name of the bucket where the audit events would be logged. |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `groupPath` | [`ID!`](#id) | Group path. |
+| `name` | [`String`](#string) | Destination name. |
+| `secretAccessKey` | [`String!`](#string) | Secret access key of the Amazon S3 account. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `amazonS3Configuration` | [`AmazonS3ConfigurationType`](#amazons3configurationtype) | configuration created. |
+| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+
### `Mutation.approveDeployment`
Input type: `ApproveDeploymentInput`
@@ -13290,6 +13314,21 @@ An endpoint and credentials used to accept Prometheus alerts for a project.
| `type` | [`AlertManagementIntegrationType!`](#alertmanagementintegrationtype) | Type of integration. |
| `url` | [`String`](#string) | Endpoint which accepts alert notifications. |
+### `AmazonS3ConfigurationType`
+
+Stores Amazon S3 configurations.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `accessKeyXid` | [`String!`](#string) | Access key ID of the Amazon S3 account. |
+| `awsRegion` | [`String!`](#string) | AWS region where the bucket is created. |
+| `bucketName` | [`String!`](#string) | Name of the bucket where the audit events would be logged. |
+| `group` | [`Group!`](#group) | Group the configuration belongs to. |
+| `id` | [`ID!`](#id) | ID of the configuration. |
+| `name` | [`String!`](#string) | Name of the external destination to send audit events to. |
+
### `ApiFuzzingCiConfiguration`
Data associated with configuring API fuzzing scans in GitLab CI.
@@ -30275,6 +30314,22 @@ Implementations:
| `type` | [`AlertManagementIntegrationType!`](#alertmanagementintegrationtype) | Type of integration. |
| `url` | [`String`](#string) | Endpoint which accepts alert notifications. |
+#### `AmazonS3ConfigurationInterface`
+
+Implementations:
+
+- [`AmazonS3ConfigurationType`](#amazons3configurationtype)
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `accessKeyXid` | [`String!`](#string) | Access key ID of the Amazon S3 account. |
+| `awsRegion` | [`String!`](#string) | AWS region where the bucket is created. |
+| `bucketName` | [`String!`](#string) | Name of the bucket where the audit events would be logged. |
+| `id` | [`ID!`](#id) | ID of the configuration. |
+| `name` | [`String!`](#string) | Name of the external destination to send audit events to. |
+
#### `BaseHeaderInterface`
Implementations:
diff --git a/doc/user/project/integrations/aws_codepipeline.md b/doc/user/project/integrations/aws_codepipeline.md
new file mode 100644
index 00000000000..b081544199e
--- /dev/null
+++ b/doc/user/project/integrations/aws_codepipeline.md
@@ -0,0 +1,114 @@
+---
+stage: Manage
+group: Import and Integrate
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# AWS CodePipeline **(FREE SAAS)**
+
+> [Introduced](https://gitlab.com/gitlab-com/alliances/aws/wip/aws-cs-collab/aws-gitlab-collaboration/-/issues/25) in GitLab 16.5.
+
+You can use your GitLab project to build, test, and deploy code changes using [AWS CodePipeline](https://aws.amazon.com/codepipeline/). To do so, you use:
+
+- AWS CodeStar Connections to connect your GitLab.com account to AWS.
+- That connection to automatically start a pipeline based on changes to your code.
+
+## Create a connection from AWS CodePipeline to GitLab
+
+Prerequisites:
+
+- You must have the Owner role on the GitLab project that you are connecting with AWS CodePipeline.
+- You must have the appropriate permissions to create a connection in AWS.
+- You must use a supported AWS region. Unsupported regions (also listed in the [AWS documentation](https://docs.aws.amazon.com/codepipeline/latest/userguide/connections-gitlab.html)) are:
+ - Asia Pacific (Hong Kong).
+ - Africa (Cape Town).
+ - Middle East (Bahrain).
+ - Europe (Zurich).
+ - AWS GovCloud (US-West and US-East).
+
+To create a connection to a project on GitLab.com, you can use either the AWS Management Console, or the AWS Command Line Interface (AWS CLI).
+
+### Use the AWS Management Console
+
+To connect a new or existing pipeline in AWS CodePipeline with GitLab.com, first authorize the AWS connection to use your GitLab account.
+
+1. Sign in to the AWS Management Console, and open the [AWS Developer Tools console](https://console.aws.amazon.com/codesuite/settings/connections).
+1. Select **Settings** > **Connections** > **Create connection**.
+1. In **Select a provider**, select **GitLab**.
+1. In **Connection name**, enter the name for the connection that you want to create and select **Connect to GitLab**.
+1. In the GitLab sign-in page, enter your credentials and select **Sign in**.
+1. An authorization page displays with a message requesting authorization for the connection to access your GitLab account. Select **Authorize**.
+1. The browser returns to the connections console page. In the **Create GitLab connection** section, the new connection is shown in **Connection name**.
+1. Select **Connect to GitLab**. After the connection is created successfully, a success banner displays. The connection details are shown on the **Connection settings** page.
+
+Now you've connected AWS CodeSuite to GitLab.com, you can create or edit a pipeline in AWS CodePipeline that leverages your GitLab projects.
+
+1. Sign in to the [AWS CodePipeline console](https://console.aws.amazon.com/codesuite/codepipeline/start).
+1. Create or edit a pipeline:
+ - If you are creating a pipeline:
+ - Complete the fields in the first screen and select **Next**.
+ - On the **Source** page, in the **Source Provider** section, select **GitLab**.
+ - If you are editing an existing pipeline:
+ - Select **Edit** > **Edit stage** to add or edit your source action.
+ - On the **Edit action** page, in the **Action name** section, enter the name for your action.
+ - In **Action provider**, select **GitLab**.
+1. In **Connection**, select the connection you created earlier.
+1. In **Repository name**, to choose the name of your GitLab project, specify the full project path with the namespace and all subgroups.
+ For example, for a group-level project, enter the project name in the following format: `group-name/subgroup-name/project-name`.
+ The project path with the namespace is in the URL in GitLab. Do not copy URLs from the Web IDE or raw views as they contain other special URL segments.
+ You can also pick an option from the dialog, or type a new path manually.
+ For more information about the:
+ - Path and namespace, see the `path_with_namespace` field in the [projects API](../../../api/projects.md#get-single-project).
+ - Namespace in GitLab, see [namespaces](../../namespace/index.md).
+
+1. In **Branch name**, select the branch where you want your pipeline to detect source changes.
+ If the branch name does not populate automatically, this might be because of one of the following:
+ - You do not have the Owner role for the project.
+ - The project name is not valid.
+ - The connection used does not have access to the project.
+
+1. In **Output artifact format**, select the format for your artifacts. To store:
+ - Output artifacts from the GitLab action using the default method, select **CodePipeline default**. The action accesses the files from the GitLab repository and
+ stores the artifacts in a ZIP file in the pipeline artifact store.
+ - A JSON file that contains a URL reference to the repository so that downstream actions can perform Git commands directly, select **Full clone**. This option can only be used
+ by CodeBuild downstream actions. To choose this option:
+ - [Update the permissions for your CodeBuild project service role](https://docs.aws.amazon.com/codepipeline/latest/userguide/troubleshooting.html#codebuild-role-connections).
+ - Follow the [AWS CodePipeline tutorial on how to use full clone with a GitHub pipeline source](https://docs.aws.amazon.com/codepipeline/latest/userguide/tutorials-github-gitclone.html).
+1. Save the source action and continue.
+
+### Use the AWS CLI
+
+To use the AWS CLI to create a connection:
+
+- Use the `create-connection` command.
+- Go to the AWS Console to authenticate with your GitLab.com account.
+- Connect your GitLab project to AWS CodePipeline.
+
+To use the `create-connection` command:
+
+1. Open a terminal (Linux, macOS, or Unix) or command prompt (Windows). Use the AWS CLI to run the `create-connection` command,
+ specifying the `--provider-type` and `--connection-name` for your connection. In this example, the third-party provider name is
+ `GitLab` and the specified connection name is `MyConnection`.
+
+ ```shell
+ aws codestar-connections create-connection --provider-type GitLab --connection-name MyConnection
+ ```
+
+ If successful, this command returns the connection's Amazon Resource Name (ARN) information. For example:
+
+ ```json
+ {
+ "ConnectionArn": "arn:aws:codestar-connections:us-west-2:account_id:connection/aEXAMPLE-8aad-4d5d-8878-dfcab0bc441f"
+ }
+ ```
+
+1. The new connection is created with a `PENDING` status by default. Use the console to change the connection's status to `AVAILABLE`.
+
+1. [Use the AWS Console to complete the connection](#use-the-aws-management-console). Make sure you select your pending GitLab connection. Do not select **Create connection**.
+
+## Related topics
+
+- [Announcement that AWS CodePipeline supports GitLab](https://aws.amazon.com/about-aws/whats-new/2023/08/aws-codepipeline-supports-gitlab/)
+- [GitLab connections - AWS CodePipeline](https://docs.aws.amazon.com/codepipeline/latest/userguide/connections-gitlab.html)
+- [Create a connection to GitLab - Developer Tools console](https://docs.aws.amazon.com/dtconsole/latest/userguide/connections-create-gitlab.html)
+- [CodeStarSourceConnection for Bitbucket, GitHub, GitHub Enterprise Server, and GitLab actions - AWS CodePipeline](https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-CodestarConnectionSource.html)
diff --git a/lib/gitlab/checks/security/policy_check.rb b/lib/gitlab/checks/security/policy_check.rb
new file mode 100644
index 00000000000..b2be393351a
--- /dev/null
+++ b/lib/gitlab/checks/security/policy_check.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Checks
+ module Security
+ class PolicyCheck < BaseSingleChecker
+ def validate!; end
+ end
+ end
+ end
+end
+
+Gitlab::Checks::Security::PolicyCheck.prepend_mod
diff --git a/lib/gitlab/checks/single_change_access.rb b/lib/gitlab/checks/single_change_access.rb
index 9f427e98e55..625524cf2bc 100644
--- a/lib/gitlab/checks/single_change_access.rb
+++ b/lib/gitlab/checks/single_change_access.rb
@@ -54,6 +54,7 @@ module Gitlab
Gitlab::Checks::PushCheck.new(self).validate!
Gitlab::Checks::BranchCheck.new(self).validate!
Gitlab::Checks::TagCheck.new(self).validate!
+ Gitlab::Checks::Security::PolicyCheck.new(self).validate!
end
def commits_check
diff --git a/lib/gitlab/pages/cache_control.rb b/lib/gitlab/pages/cache_control.rb
deleted file mode 100644
index 81da34f1219..00000000000
--- a/lib/gitlab/pages/cache_control.rb
+++ /dev/null
@@ -1,105 +0,0 @@
-# frozen_string_literal: true
-
-require 'set'
-
-module Gitlab
- module Pages
- class CacheControl
- include Gitlab::Utils::StrongMemoize
-
- EXPIRE = 12.hours
- # To avoid delivering expired deployment URL in the cached payload,
- # use a longer expiration time in the deployment URL
- DEPLOYMENT_EXPIRATION = (EXPIRE + 12.hours)
-
- SETTINGS_CACHE_KEY = 'pages_domain_for_%{type}_%{id}'
- PAYLOAD_CACHE_KEY = '%{settings_cache_key}_%{settings_hash}'
-
- class << self
- def for_domain(domain_id)
- new(type: :domain, id: domain_id)
- end
-
- def for_namespace(namespace_id)
- new(type: :namespace, id: namespace_id)
- end
- end
-
- def initialize(type:, id:)
- raise(ArgumentError, "type must be :namespace or :domain") unless %i[namespace domain].include?(type)
-
- @type = type
- @id = id
- end
-
- def cache_key
- strong_memoize(:payload_cache_key) do
- cache_settings_hash!
-
- payload_cache_key_for(settings_hash)
- end
- end
-
- # Invalidates the cache.
- #
- # Since rails nodes and sidekiq nodes have different application settings,
- # and the invalidation happens in a sidekiq node, we have to use the
- # cached settings hash to build the payload cache key to be invalidated.
- def clear_cache
- keys = cached_settings_hashes
- .map { |hash| payload_cache_key_for(hash) }
- .push(settings_cache_key)
-
- ::Gitlab::AppLogger.info(
- message: 'clear pages cache',
- pages_keys: keys,
- pages_type: @type,
- pages_id: @id
- )
-
- Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
- Rails.cache.delete_multi(keys)
- end
- end
-
- private
-
- # Since rails nodes and sidekiq nodes have different application settings,
- # we cache the application settings hash when creating the payload cache
- # so we can use these values to invalidate the cache in a sidekiq node later.
- def cache_settings_hash!
- cached = cached_settings_hashes.to_set
- Rails.cache.write(settings_cache_key, cached.add(settings_hash))
- end
-
- def cached_settings_hashes
- Rails.cache.read(settings_cache_key) || []
- end
-
- def payload_cache_key_for(settings_hash)
- PAYLOAD_CACHE_KEY % {
- settings_cache_key: settings_cache_key,
- settings_hash: settings_hash
- }
- end
-
- def settings_cache_key
- strong_memoize(:settings_cache_key) do
- SETTINGS_CACHE_KEY % { type: @type, id: @id }
- end
- end
-
- def settings_hash
- strong_memoize(:settings_hash) do
- values = ::Gitlab.config.pages.dup
-
- values['app_settings'] = ::Gitlab::CurrentSettings.attributes.slice(
- 'force_pages_access_control'
- )
-
- ::Digest::SHA256.hexdigest(values.inspect)
- end
- end
- end
- end
-end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d4e5fe8d58a..ff23f14ecb3 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2239,6 +2239,12 @@ msgstr ""
msgid "AbuseReportEvent|Successfully scheduled the user for deletion and closed the report"
msgstr ""
+msgid "AbuseReportEvent|Successfully trusted the user"
+msgstr ""
+
+msgid "AbuseReportEvent|Successfully trusted the user and closed the report"
+msgstr ""
+
msgid "AbuseReports|%{reportedUser} reported for %{category} by %{count} users"
msgstr ""
@@ -2308,6 +2314,9 @@ msgstr ""
msgid "AbuseReport|Confirmed spam"
msgstr ""
+msgid "AbuseReport|Confirmed trusted user"
+msgstr ""
+
msgid "AbuseReport|Confirmed violation of a copyright or a trademark"
msgstr ""
@@ -2428,6 +2437,9 @@ msgstr ""
msgid "AbuseReport|Tier"
msgstr ""
+msgid "AbuseReport|Trust user"
+msgstr ""
+
msgid "AbuseReport|Verification"
msgstr ""
diff --git a/rubocop/rubocop-ruby31.yml b/rubocop/rubocop-ruby31.yml
index 109c7ca2dfe..712c5a8f447 100644
--- a/rubocop/rubocop-ruby31.yml
+++ b/rubocop/rubocop-ruby31.yml
@@ -2,6 +2,7 @@
# Ruby 3.1.
#
# After the transition has been completed:
+# * Enable all disabled cops and resolve the offenses.
# * Move all configuration which enabled or tweaked cops to .rubocop.yml.
# * Remove all remaining configuration.
diff --git a/rubocop/rubocop-ruby32.yml b/rubocop/rubocop-ruby32.yml
new file mode 100644
index 00000000000..0d0e8a46c60
--- /dev/null
+++ b/rubocop/rubocop-ruby32.yml
@@ -0,0 +1,15 @@
+# RuboCop configuration adjustments during the transition time from Ruby 3.1 to
+# Ruby 3.2.
+#
+# After the transition has been completed:
+# * Enable all disabled cops and resolve the offenses.
+# * Move all configuration which enabled or tweaked cops to .rubocop.yml.
+# * Remove all remaining configuration.
+
+# Short-hand Hash syntax does not work prior 3.1.
+Style/HashSyntax:
+ EnforcedShorthandSyntax: never
+
+# With 3.2+ `require 'set'` is no longer needed as it's always loaded.
+Lint/RedundantRequireStatement:
+ Enabled: false
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
index d61d5cc2d78..de2b5159fe7 100644
--- a/spec/factories/users.rb
+++ b/spec/factories/users.rb
@@ -48,6 +48,15 @@ FactoryBot.define do
after(:build) { |user, _| user.ban! }
end
+ trait :trusted do
+ after(:create) do |user, _|
+ user.custom_attributes.create!(
+ key: UserCustomAttribute::TRUSTED_BY,
+ value: "placeholder"
+ )
+ end
+ end
+
trait :ldap_blocked do
after(:build) { |user, _| user.ldap_block! }
end
diff --git a/spec/frontend/admin/abuse_report/components/report_actions_spec.js b/spec/frontend/admin/abuse_report/components/report_actions_spec.js
index 0e20630db14..3c366980c14 100644
--- a/spec/frontend/admin/abuse_report/components/report_actions_spec.js
+++ b/spec/frontend/admin/abuse_report/components/report_actions_spec.js
@@ -17,6 +17,9 @@ import {
ERROR_MESSAGE,
NO_ACTION,
USER_ACTION_OPTIONS,
+ TRUST_ACTION,
+ TRUST_REASON,
+ REASON_OPTIONS,
} from '~/admin/abuse_report/constants';
import { mockAbuseReport } from '../mock_data';
@@ -40,10 +43,11 @@ describe('ReportActions', () => {
const setCloseReport = (close) => wrapper.findByTestId('close').find('input').setChecked(close);
const setSelectOption = (id, value) =>
wrapper.findByTestId(`${id}-select`).find(`option[value=${value}]`).setSelected();
- const selectAction = (action) => setSelectOption('action', action);
+ const selectAction = (chosenAction) => setSelectOption('action', chosenAction);
const selectReason = (reason) => setSelectOption('reason', reason);
const setComment = (comment) => wrapper.findByTestId('comment').find('input').setValue(comment);
const submitForm = () => wrapper.findByTestId('submit-button').vm.$emit('click');
+ const findReasonOptions = () => wrapper.findByTestId('reason-select');
const createComponent = (props = {}) => {
wrapper = mountExtended(ReportActions, {
@@ -79,8 +83,8 @@ describe('ReportActions', () => {
expect(options).toHaveLength(USER_ACTION_OPTIONS.length);
- USER_ACTION_OPTIONS.forEach((action, index) => {
- expect(options.at(index).text()).toBe(action.text);
+ USER_ACTION_OPTIONS.forEach((userAction, index) => {
+ expect(options.at(index).text()).toBe(userAction.text);
});
});
});
@@ -100,6 +104,51 @@ describe('ReportActions', () => {
});
});
+ describe('reasons', () => {
+ beforeEach(() => {
+ clickActionsButton();
+ });
+
+ it('shows all non-trust reasons by default', () => {
+ const reasons = findReasonOptions().findAll('option');
+ expect(reasons).toHaveLength(REASON_OPTIONS.length);
+
+ REASON_OPTIONS.forEach((reason, index) => {
+ expect(reasons.at(index).text()).toBe(reason.text);
+ });
+ });
+
+ describe('when user selects any non-trust action', () => {
+ it('shows non-trust reasons', () => {
+ const reasonLength = REASON_OPTIONS.length;
+ let reasons;
+
+ USER_ACTION_OPTIONS.forEach((userAction) => {
+ if (userAction !== TRUST_ACTION && userAction !== NO_ACTION) {
+ selectAction(userAction.value);
+
+ reasons = findReasonOptions().findAll('option');
+ expect(reasons).toHaveLength(reasonLength);
+ }
+ });
+ });
+ });
+
+ describe('when user selects "Trust user"', () => {
+ beforeEach(() => {
+ selectAction(TRUST_ACTION.value);
+ });
+
+ it('only shows "Confirmed trusted user" reason', () => {
+ const reasons = findReasonOptions().findAll('option');
+
+ expect(reasons).toHaveLength(1);
+
+ expect(reasons.at(0).text()).toBe(TRUST_REASON.text);
+ });
+ });
+ });
+
describe('when clicking the actions button', () => {
beforeEach(() => {
clickActionsButton();
diff --git a/spec/models/integrations/bamboo_spec.rb b/spec/models/integrations/bamboo_spec.rb
index 50e131a0845..62080fa7a12 100644
--- a/spec/models/integrations/bamboo_spec.rb
+++ b/spec/models/integrations/bamboo_spec.rb
@@ -116,7 +116,7 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching, feat
is_expected.to eq('http://gitlab.com/bamboo/browse/42')
end
- context 'bamboo_url has trailing slash' do
+ context 'when bamboo_url has trailing slash' do
let(:bamboo_url) { 'http://gitlab.com/bamboo/' }
it 'returns a build URL' do
@@ -198,7 +198,8 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching, feat
context 'when Bamboo API returns an array of results and we only consider the last one' do
let(:bamboo_response_template) do
- %q({"results":{"results":{"size":"2","result":[{"buildState":"%{build_state}","planResultKey":{"key":"41"}},{"buildState":"%{build_state}","planResultKey":{"key":"42"}}]}}})
+ '{"results":{"results":{"size":"2","result":[{"buildState":"%{build_state}","planResultKey":{"key":"41"}}, ' \
+ '{"buildState":"%{build_state}","planResultKey":{"key":"42"}}]}}}'
end
it_behaves_like 'reactive cache calculation'
@@ -207,7 +208,9 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching, feat
describe '#avatar_url' do
it 'returns the avatar image path' do
- expect(subject.avatar_url).to eq(ActionController::Base.helpers.image_path('illustrations/third-party-logos/integrations-logos/atlassian-bamboo.svg'))
+ expect(subject.avatar_url).to eq(ActionController::Base.helpers.image_path(
+ 'illustrations/third-party-logos/integrations-logos/atlassian-bamboo.svg'
+ ))
end
end
@@ -228,11 +231,11 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching, feat
status: status,
headers: { 'Content-Type' => 'application/json' },
body: body
- ).with(basic_auth: %w(mic password))
+ ).with(basic_auth: %w[mic password])
end
def bamboo_response(build_state: 'success')
# reference: https://docs.atlassian.com/atlassian-bamboo/REST/6.2.5/#d2e786
- bamboo_response_template % { build_state: build_state }
+ format(bamboo_response_template, build_state: build_state)
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index c611c3c26e3..d35449ca8c8 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -6120,25 +6120,23 @@ RSpec.describe User, feature_category: :user_profile do
end
end
- describe '#allow_possible_spam?' do
+ describe '#trusted?' do
context 'when no custom attribute is set' do
- it 'is false' do
- expect(user.allow_possible_spam?).to be_falsey
+ it 'is falsey' do
+ expect(user.trusted?).to be_falsey
end
end
context 'when the custom attribute is set' do
before do
- user.custom_attributes.upsert_custom_attributes(
- [{
- user_id: user.id,
- key: UserCustomAttribute::ALLOW_POSSIBLE_SPAM,
- value: "test"
- }])
+ user.custom_attributes.create!(
+ key: UserCustomAttribute::TRUSTED_BY,
+ value: "test"
+ )
end
- it '#allow_possible_spam? is true' do
- expect(user.allow_possible_spam?).to be_truthy
+ it 'is truthy' do
+ expect(user.trusted?).to be_truthy
end
end
end
diff --git a/spec/services/admin/abuse_reports/moderate_user_service_spec.rb b/spec/services/admin/abuse_reports/moderate_user_service_spec.rb
index 7e08db2b612..3b80d3276a2 100644
--- a/spec/services/admin/abuse_reports/moderate_user_service_spec.rb
+++ b/spec/services/admin/abuse_reports/moderate_user_service_spec.rb
@@ -210,6 +210,43 @@ RSpec.describe Admin::AbuseReports::ModerateUserService, feature_category: :inst
end
end
+ describe 'when trusting the user' do
+ let(:action) { 'trust_user' }
+
+ it 'calls the Users::TrustService method' do
+ expect_next_instance_of(Users::TrustService, admin) do |service|
+ expect(service).to receive(:execute).with(abuse_report.user).and_return(status: :success)
+ end
+
+ subject
+ end
+
+ context 'when not closing the report' do
+ let(:close) { false }
+
+ it_behaves_like 'does not close the report'
+ it_behaves_like 'records an event', action: 'trust_user'
+ end
+
+ context 'when closing the report' do
+ it_behaves_like 'closes the report'
+ it_behaves_like 'records an event', action: 'trust_user_and_close_report'
+ end
+
+ context 'when trusting the user fails' do
+ before do
+ allow_next_instance_of(Users::TrustService) do |service|
+ allow(service).to receive(:execute).with(abuse_report.user)
+ .and_return(status: :error, message: 'Trusting the user failed')
+ end
+ end
+
+ it_behaves_like 'returns an error response', 'Trusting the user failed'
+ it_behaves_like 'does not close the report'
+ it_behaves_like 'does not record an event'
+ end
+ end
+
describe 'when only closing the report' do
let(:action) { '' }
diff --git a/spec/services/spam/spam_verdict_service_spec.rb b/spec/services/spam/spam_verdict_service_spec.rb
index 70f43d82ead..361742699b0 100644
--- a/spec/services/spam/spam_verdict_service_spec.rb
+++ b/spec/services/spam/spam_verdict_service_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe Spam::SpamVerdictService, feature_category: :instance_resiliency
end
let(:check_for_spam) { true }
- let_it_be(:user) { create(:user) }
+ let_it_be_with_reload(:user) { create(:user) }
let_it_be(:issue) { create(:issue, author: user) }
let_it_be(:snippet) { create(:personal_snippet, :public, author: user) }
@@ -136,15 +136,9 @@ RSpec.describe Spam::SpamVerdictService, feature_category: :instance_resiliency
end
end
- context 'if allow_possible_spam user custom attribute is set' do
+ context 'if user is trusted to create possible spam' do
before do
- UserCustomAttribute.upsert_custom_attributes(
- [{
- user_id: user.id,
- key: 'allow_possible_spam',
- value: 'does not matter'
- }]
- )
+ user.custom_attributes.create!(key: 'trusted_by', value: 'does not matter')
end
context 'and a service returns a verdict that should be overridden' do
diff --git a/spec/services/users/allow_possible_spam_service_spec.rb b/spec/services/users/trust_service_spec.rb
similarity index 80%
rename from spec/services/users/allow_possible_spam_service_spec.rb
rename to spec/services/users/trust_service_spec.rb
index 53618f0c8e9..1f71992ce9b 100644
--- a/spec/services/users/allow_possible_spam_service_spec.rb
+++ b/spec/services/users/trust_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::AllowPossibleSpamService, feature_category: :user_management do
+RSpec.describe Users::TrustService, feature_category: :user_management do
let_it_be(:current_user) { create(:admin) }
subject(:service) { described_class.new(current_user) }
@@ -18,7 +18,7 @@ RSpec.describe Users::AllowPossibleSpamService, feature_category: :user_manageme
operation
user.reload
- expect(user.custom_attributes.by_key(UserCustomAttribute::ALLOW_POSSIBLE_SPAM)).to be_present
+ expect(user.custom_attributes.by_key(UserCustomAttribute::TRUSTED_BY)).to be_present
end
end
end
diff --git a/spec/services/users/disallow_possible_spam_service_spec.rb b/spec/services/users/untrust_service_spec.rb
similarity index 66%
rename from spec/services/users/disallow_possible_spam_service_spec.rb
rename to spec/services/users/untrust_service_spec.rb
index 32a47e05525..054cb9b82dc 100644
--- a/spec/services/users/disallow_possible_spam_service_spec.rb
+++ b/spec/services/users/untrust_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::DisallowPossibleSpamService, feature_category: :user_management do
+RSpec.describe Users::UntrustService, feature_category: :user_management do
let_it_be(:current_user) { create(:admin) }
subject(:service) { described_class.new(current_user) }
@@ -16,19 +16,19 @@ RSpec.describe Users::DisallowPossibleSpamService, feature_category: :user_manag
UserCustomAttribute.upsert_custom_attributes(
[{
user_id: user.id,
- key: :allow_possible_spam,
+ key: UserCustomAttribute::TRUSTED_BY,
value: 'not important'
}]
)
end
it 'updates the custom attributes', :aggregate_failures do
- expect(user.custom_attributes.by_key(UserCustomAttribute::ALLOW_POSSIBLE_SPAM)).to be_present
+ expect(user.trusted_with_spam_attribute).to be_present
operation
user.reload
- expect(user.custom_attributes).to be_empty
+ expect(user.trusted_with_spam_attribute).to be nil
end
end
end
diff --git a/workhorse/go.mod b/workhorse/go.mod
index adec9df0daf..c1f99cfcdd9 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -1,6 +1,6 @@
module gitlab.com/gitlab-org/gitlab/workhorse
-go 1.18
+go 1.19
require (
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0