diff --git a/app/models/concerns/has_user_type.rb b/app/models/concerns/has_user_type.rb index ccb5bebcc39c0a6c5880b012ab3fe67995125762..b5352db5a566220afec434d9fa574ebadc2fb235 100644 --- a/app/models/concerns/has_user_type.rb +++ b/app/models/concerns/has_user_type.rb @@ -14,10 +14,11 @@ module HasUserType migration_bot: 7, security_bot: 8, automation_bot: 9, - security_policy_bot: 10 + security_policy_bot: 10, + admin_bot: 11 }.with_indifferent_access.freeze - BOT_USER_TYPES = %w[alert_bot project_bot support_bot visual_review_bot migration_bot security_bot automation_bot security_policy_bot].freeze + BOT_USER_TYPES = %w[alert_bot project_bot support_bot visual_review_bot migration_bot security_bot automation_bot security_policy_bot admin_bot].freeze NON_INTERNAL_USER_TYPES = %w[human project_bot service_user].freeze INTERNAL_USER_TYPES = (USER_TYPES.keys - NON_INTERNAL_USER_TYPES).freeze diff --git a/app/models/user.rb b/app/models/user.rb index dae7ea75583381fc41c5949b9aff54c684bfbf95..0916a89e4d2f784c115d43795bbfe6c4b45d1a39 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -906,6 +906,17 @@ def security_policy_bot end end + def admin_bot + email_pattern = "admin-bot%s@#{Settings.gitlab.host}" + + unique_internal(where(user_type: :admin_bot), 'GitLab-Admin-Bot', email_pattern) do |u| + u.bio = 'Admin bot used for tasks that require admin privileges' + u.name = 'GitLab Admin Bot' + u.avatar = bot_avatar(image: 'admin-bot.png') + u.admin = true + end + end + # Return true if there is only single non-internal user in the deployment, # ghost user is ignored. def single_user? diff --git a/app/workers/projects/inactive_projects_deletion_cron_worker.rb b/app/workers/projects/inactive_projects_deletion_cron_worker.rb index af62efeb0891e735c2a0a2dbb92d09d1b95884eb..31fdb3d961589f40f9b2d98910a365d3622ad442 100644 --- a/app/workers/projects/inactive_projects_deletion_cron_worker.rb +++ b/app/workers/projects/inactive_projects_deletion_cron_worker.rb @@ -22,9 +22,9 @@ def perform return unless ::Gitlab::CurrentSettings.delete_inactive_projects? @start_time ||= ::Gitlab::Metrics::System.monotonic_time - admin_user = User.admins.active.first + admin_bot = ::User.admin_bot - return unless admin_user + return unless admin_bot notified_inactive_projects = Gitlab::InactiveProjectsDeletionWarningTracker.notified_projects @@ -39,14 +39,14 @@ def perform raise TimeoutError end - with_context(project: project, user: admin_user) do + with_context(project: project, user: admin_bot) do deletion_warning_email_sent_on = notified_inactive_projects["project:#{project.id}"] if send_deletion_warning_email?(deletion_warning_email_sent_on, project) - send_notification(project, admin_user) + send_notification(project, admin_bot) elsif deletion_warning_email_sent_on && delete_due_to_inactivity?(deletion_warning_email_sent_on) Gitlab::InactiveProjectsDeletionWarningTracker.new(project.id).reset - delete_project(project, admin_user) + delete_project(project, admin_bot) end end end diff --git a/doc/administration/inactive_project_deletion.md b/doc/administration/inactive_project_deletion.md index 88d8e3fc6483f0eacf37354a9d9070a39ce75eef..ea5658bef8409f35046183aa916698b69499e750 100644 --- a/doc/administration/inactive_project_deletion.md +++ b/doc/administration/inactive_project_deletion.md @@ -12,7 +12,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w Administrators of large GitLab instances can find that over time, projects become inactive and are no longer used. These projects take up unnecessary disk space. With inactive project deletion, you can identify these projects, warn the maintainers ahead of time, and then delete the projects if they remain inactive. When an inactive project is -deleted, the action generates an audit event that it was performed by the first active administrator. +deleted, the action generates an audit event that it was performed by the @GitLab-Admin-Bot. For the default setting on GitLab.com, see the [GitLab.com settings page](../user/gitlab_com/index.md#inactive-project-deletion). diff --git a/ee/spec/models/ee/user_spec.rb b/ee/spec/models/ee/user_spec.rb index e58efb1f1652ba9c6be07cd59a9c83a2f817bb88..cac006701695a9d2bb45a6aa8650a15dcca5974c 100644 --- a/ee/spec/models/ee/user_spec.rb +++ b/ee/spec/models/ee/user_spec.rb @@ -650,7 +650,7 @@ AND ("users"."user_type" IS NULL OR "users"."user_type" IN (6, 4)) AND - ("users"."user_type" IS NULL OR "users"."user_type" NOT IN (2, 6, 1, 3, 7, 8, 9, 10)) + ("users"."user_type" IS NULL OR "users"."user_type" NOT IN (2, 6, 1, 3, 7, 8, 9, 10, 11)) SQL expect(users.to_sql.squish).to eq expected_sql.squish @@ -678,7 +678,7 @@ AND ("users"."user_type" IS NULL OR "users"."user_type" IN (6, 4)) AND - ("users"."user_type" IS NULL OR "users"."user_type" NOT IN (2, 6, 1, 3, 7, 8, 9, 10)) + ("users"."user_type" IS NULL OR "users"."user_type" NOT IN (2, 6, 1, 3, 7, 8, 9, 10, 11)) AND (EXISTS (SELECT 1 FROM "members" WHERE "members"."user_id" = "users"."id" diff --git a/ee/spec/workers/ee/projects/inactive_projects_deletion_cron_worker_spec.rb b/ee/spec/workers/ee/projects/inactive_projects_deletion_cron_worker_spec.rb index 4f639b7444b114924f21f26ced274612482636cd..2042c9fa93ec2e3b991b4d87586d0af93448b461 100644 --- a/ee/spec/workers/ee/projects/inactive_projects_deletion_cron_worker_spec.rb +++ b/ee/spec/workers/ee/projects/inactive_projects_deletion_cron_worker_spec.rb @@ -8,7 +8,7 @@ describe "#perform", :clean_gitlab_redis_shared_state, :sidekiq_inline do subject(:worker) { described_class.new } - let_it_be(:admin_user) { create(:user, :admin) } + let_it_be(:admin_bot) { create(:user, :admin_bot) } let_it_be(:non_admin_user) { create(:user) } let_it_be(:group) { create(:group) } let_it_be(:new_blank_project) do @@ -87,7 +87,7 @@ expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async) expect(::Projects::MarkForDeletionService).not_to receive(:perform_in) - expect(::Projects::DestroyService).to receive(:new).with(inactive_large_project, admin_user, {}) + expect(::Projects::DestroyService).to receive(:new).with(inactive_large_project, admin_bot, {}) .at_least(:once).and_call_original worker.perform @@ -120,7 +120,7 @@ expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async) expect(::Projects::MarkForDeletionService).not_to receive(:perform_in) - expect(::Projects::DestroyService).to receive(:new).with(inactive_large_project, admin_user, {}) + expect(::Projects::DestroyService).to receive(:new).with(inactive_large_project, admin_bot, {}) .at_least(:once).and_call_original worker.perform @@ -147,7 +147,7 @@ end expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async) - expect(::Projects::MarkForDeletionService).to receive(:new).with(inactive_large_project, admin_user, {}) + expect(::Projects::MarkForDeletionService).to receive(:new).with(inactive_large_project, admin_bot, {}) .and_call_original worker.perform diff --git a/lib/assets/images/bot_avatars/admin-bot.png b/lib/assets/images/bot_avatars/admin-bot.png new file mode 100644 index 0000000000000000000000000000000000000000..8418676d9b10895f472617c10e135c538bfd00c1 Binary files /dev/null and b/lib/assets/images/bot_avatars/admin-bot.png differ diff --git a/spec/models/concerns/has_user_type_spec.rb b/spec/models/concerns/has_user_type_spec.rb index 3938257708e013bedb5f9c19b5793ac27a6aec9c..964a35d4d39d8f5377a4e4e1df10b59fad145943 100644 --- a/spec/models/concerns/has_user_type_spec.rb +++ b/spec/models/concerns/has_user_type_spec.rb @@ -5,7 +5,7 @@ RSpec.describe User do specify 'types consistency checks', :aggregate_failures do expect(described_class::USER_TYPES.keys) - .to match_array(%w[human ghost alert_bot project_bot support_bot service_user security_bot visual_review_bot migration_bot automation_bot security_policy_bot]) + .to match_array(%w[human ghost alert_bot project_bot support_bot service_user security_bot visual_review_bot migration_bot automation_bot security_policy_bot admin_bot]) expect(described_class::USER_TYPES).to include(*described_class::BOT_USER_TYPES) expect(described_class::USER_TYPES).to include(*described_class::NON_INTERNAL_USER_TYPES) expect(described_class::USER_TYPES).to include(*described_class::INTERNAL_USER_TYPES) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 43541250904abe744d3a3e20e49f824350049a2f..021083dc9187d0c7e0f447ac1d1c5d0c40cb6932 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -6808,7 +6808,8 @@ def access_levels(groups) { user_type: :support_bot }, { user_type: :security_bot }, { user_type: :automation_bot }, - { user_type: :security_policy_bot } + { user_type: :security_policy_bot }, + { user_type: :admin_bot } ] end @@ -6896,6 +6897,7 @@ def access_levels(groups) 'security_bot' | false 'automation_bot' | false 'security_policy_bot' | false + 'admin_bot' | false end with_them do @@ -7045,18 +7047,26 @@ def access_levels(groups) it_behaves_like 'bot users', :ghost it_behaves_like 'bot users', :automation_bot it_behaves_like 'bot users', :security_policy_bot + it_behaves_like 'bot users', :admin_bot it_behaves_like 'bot user avatars', :alert_bot, 'alert-bot.png' it_behaves_like 'bot user avatars', :support_bot, 'support-bot.png' it_behaves_like 'bot user avatars', :security_bot, 'security-bot.png' it_behaves_like 'bot user avatars', :security_policy_bot, 'security-bot.png' it_behaves_like 'bot user avatars', :automation_bot, 'support-bot.png' + it_behaves_like 'bot user avatars', :admin_bot, 'admin-bot.png' context 'when bot is the support_bot' do subject { described_class.support_bot } it { is_expected.to be_confirmed } end + + context 'when bot is the admin bot' do + subject { described_class.admin_bot } + + it { is_expected.to be_admin } + end end describe '#confirmation_required_on_sign_in?' do diff --git a/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb b/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb index 50b5b0a6e7bad4c91c07500c0de56b0bc1530b2d..f3c6434dc85404678f391ecfda0d8e9836d7da6a 100644 --- a/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb +++ b/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb @@ -36,7 +36,7 @@ describe "#perform" do subject(:worker) { described_class.new } - let_it_be(:admin_user) { create(:user, :admin) } + let_it_be(:admin_bot) { create(:user, :admin_bot) } let_it_be(:non_admin_user) { create(:user) } let_it_be(:new_blank_project) do create_project_with_statistics.tap do |project| @@ -121,7 +121,7 @@ end expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async) - expect(::Projects::DestroyService).to receive(:new).with(inactive_large_project, admin_user, {}) + expect(::Projects::DestroyService).to receive(:new).with(inactive_large_project, admin_bot, {}) .at_least(:once).and_call_original worker.perform