diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-17 19:05:49 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-05-17 19:05:49 +0300 |
commit | 43a25d93ebdabea52f99b05e15b06250cd8f07d7 (patch) | |
tree | dceebdc68925362117480a5d672bcff122fb625b /app/controllers | |
parent | 20c84b99005abd1c82101dfeff264ac50d2df211 (diff) |
Add latest changes from gitlab-org/gitlab@16-0-stable-eev16.0.0-rc42
Diffstat (limited to 'app/controllers')
206 files changed, 1448 insertions, 1328 deletions
diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index f6b4fbac8d5..edeac57bc42 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -55,7 +55,7 @@ class AbuseReportsController < ApplicationController private def report_params - params.require(:abuse_report).permit(:message, :user_id, :category, :reported_from_url, links_to_spam: []) + params.require(:abuse_report).permit(:message, :user_id, :category, :reported_from_url, :screenshot, links_to_spam: []) end # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/controllers/admin/abuse_reports_controller.rb b/app/controllers/admin/abuse_reports_controller.rb index 5357558434e..84e5cc430ef 100644 --- a/app/controllers/admin/abuse_reports_controller.rb +++ b/app/controllers/admin/abuse_reports_controller.rb @@ -3,16 +3,37 @@ class Admin::AbuseReportsController < Admin::ApplicationController feature_category :insider_threat + before_action :set_status_param, only: :index, if: -> { Feature.enabled?(:abuse_reports_list) } + before_action :find_abuse_report, only: [:show, :update, :destroy] + def index @abuse_reports = AbuseReportsFinder.new(params).execute end - def destroy - abuse_report = AbuseReport.find(params[:id]) + def show; end + + def update + Admin::AbuseReportUpdateService.new(@abuse_report, current_user, permitted_params).execute + end - abuse_report.remove_user(deleted_by: current_user) if params[:remove_user] - abuse_report.destroy + def destroy + @abuse_report.remove_user(deleted_by: current_user) if params[:remove_user] + @abuse_report.destroy head :ok end + + private + + def find_abuse_report + @abuse_report = AbuseReport.find(params[:id]) + end + + def set_status_param + params[:status] ||= 'open' + end + + def permitted_params + params.permit(:user_action, :close, :reason, :comment) + end end diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index ade58ca0970..dff1c04311d 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -13,22 +13,16 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController before_action :disable_query_limiting, only: [:usage_data] + before_action do + push_frontend_feature_flag(:ci_variables_pages, current_user) + end + feature_category :not_owned, [ # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned :general, :reporting, :metrics_and_profiling, :network, :preferences, :update, :reset_health_check_token ] - feature_category :metrics, [ - :create_self_monitoring_project, - :status_create_self_monitoring_project, - :delete_self_monitoring_project, - :status_delete_self_monitoring_project - ] urgency :low, [ - :create_self_monitoring_project, - :status_create_self_monitoring_project, - :delete_self_monitoring_project, - :status_delete_self_monitoring_project, :reset_error_tracking_access_token ] @@ -101,8 +95,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController def reset_error_tracking_access_token @application_setting.reset_error_tracking_access_token! - redirect_to general_admin_application_settings_path, - notice: _('New error tracking access token has been generated!') + redirect_to general_admin_application_settings_path, notice: _('New error tracking access token has been generated!') end def clear_repository_check_states @@ -121,91 +114,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController redirect_to ::Gitlab::LetsEncrypt.terms_of_service_url end - # Specs are in spec/requests/self_monitoring_project_spec.rb - def create_self_monitoring_project - job_id = SelfMonitoringProjectCreateWorker.with_status.perform_async # rubocop:disable CodeReuse/Worker - - render status: :accepted, json: { - job_id: job_id, - monitor_status: status_create_self_monitoring_project_admin_application_settings_path - } - end - - # Specs are in spec/requests/self_monitoring_project_spec.rb - def status_create_self_monitoring_project - job_id = params[:job_id].to_s - - unless job_id.length <= PARAM_JOB_ID_MAX_SIZE - return render status: :bad_request, json: { - message: format(_('Parameter "job_id" cannot exceed length of %{job_id_max_size}'), job_id_max_size: PARAM_JOB_ID_MAX_SIZE) - } - end - - if SelfMonitoringProjectCreateWorker.in_progress?(job_id) # rubocop:disable CodeReuse/Worker - ::Gitlab::PollingInterval.set_header(response, interval: 3_000) - - return render status: :accepted, json: { - message: _('Job to create self-monitoring project is in progress') - } - end - - return render status: :ok, json: self_monitoring_data if @application_setting.self_monitoring_project_id.present? - - render status: :bad_request, json: { - message: _('Self-monitoring project does not exist. Please check logs ' \ - 'for any error messages') - } - end - - # Specs are in spec/requests/self_monitoring_project_spec.rb - def delete_self_monitoring_project - job_id = SelfMonitoringProjectDeleteWorker.with_status.perform_async # rubocop:disable CodeReuse/Worker - - render status: :accepted, json: { - job_id: job_id, - monitor_status: status_delete_self_monitoring_project_admin_application_settings_path - } - end - - # Specs are in spec/requests/self_monitoring_project_spec.rb - def status_delete_self_monitoring_project - job_id = params[:job_id].to_s - - unless job_id.length <= PARAM_JOB_ID_MAX_SIZE - return render status: :bad_request, json: { - message: format(_('Parameter "job_id" cannot exceed length of %{job_id_max_size}'), job_id_max_size: PARAM_JOB_ID_MAX_SIZE) - } - end - - if SelfMonitoringProjectDeleteWorker.in_progress?(job_id) # rubocop:disable CodeReuse/Worker - ::Gitlab::PollingInterval.set_header(response, interval: 3_000) - - return render status: :accepted, json: { - message: _('Job to delete self-monitoring project is in progress') - } - end - - if @application_setting.self_monitoring_project_id.nil? - return render status: :ok, json: { - message: _('Self-monitoring project has been successfully deleted') - } - end - - render status: :bad_request, json: { - message: _('Self-monitoring project was not deleted. Please check logs ' \ - 'for any error messages') - } - end - private - def self_monitoring_data - { - project_id: @application_setting.self_monitoring_project_id, - project_full_path: @application_setting.self_monitoring_project&.full_path - } - end - def set_application_setting @application_setting = ApplicationSetting.current_without_cache @plans = Plan.all @@ -231,6 +141,9 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController params[:application_setting][:valid_runner_registrars]&.delete("") params[:application_setting][:restricted_visibility_levels]&.delete("") + params[:application_setting][:package_metadata_purl_types]&.delete("") + params[:application_setting][:package_metadata_purl_types]&.map!(&:to_i) + if params[:application_setting].key?(:required_instance_ci_template) if params[:application_setting][:required_instance_ci_template].empty? params[:application_setting][:required_instance_ci_template] = nil @@ -273,6 +186,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :default_branch_name, disabled_oauth_sign_in_sources: [], import_sources: [], + package_metadata_purl_types: [], restricted_visibility_levels: [], repository_storages_weighted: {}, valid_runner_registrars: [] diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb index d66b3cb4366..d97fcc5df74 100644 --- a/app/controllers/admin/applications_controller.rb +++ b/app/controllers/admin/applications_controller.rb @@ -3,19 +3,17 @@ class Admin::ApplicationsController < Admin::ApplicationController include OauthApplications - before_action :set_application, only: [:show, :edit, :update, :destroy] + before_action :set_application, only: [:show, :edit, :update, :renew, :destroy] before_action :load_scopes, only: [:new, :create, :edit, :update] - feature_category :authentication_and_authorization + feature_category :system_access def index applications = ApplicationsFinder.new.execute @applications = Kaminari.paginate_array(applications).page(params[:page]) end - def show - @created = get_created_session if Feature.disabled?('hash_oauth_secrets') - end + def show; end def new @application = Doorkeeper::Application.new @@ -30,14 +28,8 @@ class Admin::ApplicationsController < Admin::ApplicationController if @application.persisted? flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) - if Feature.enabled?('hash_oauth_secrets') - @created = true - render :show - else - set_created_session - - redirect_to admin_application_url(@application) - end + @created = true + render :show else render :new end @@ -51,6 +43,16 @@ class Admin::ApplicationsController < Admin::ApplicationController end end + def renew + @application.renew_secret + + if @application.save + render json: { secret: @application.plaintext_secret } + else + render json: { errors: @application.errors }, status: :unprocessable_entity + end + end + def destroy @application.destroy redirect_to admin_applications_url, status: :found, notice: _('Application was successfully destroyed.') diff --git a/app/controllers/admin/background_migrations_controller.rb b/app/controllers/admin/background_migrations_controller.rb index b904196c5ab..a5211961d81 100644 --- a/app/controllers/admin/background_migrations_controller.rb +++ b/app/controllers/admin/background_migrations_controller.rb @@ -10,6 +10,7 @@ module Admin def index @relations_by_tab = { 'queued' => batched_migration_class.queued.queue_order, + 'finalizing' => batched_migration_class.finalizing.queue_order, 'failed' => batched_migration_class.with_status(:failed).queue_order, 'finished' => batched_migration_class.with_status(:finished).queue_order.reverse_order } diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb index d641a26c9fb..821c3cc1635 100644 --- a/app/controllers/admin/broadcast_messages_controller.rb +++ b/app/controllers/admin/broadcast_messages_controller.rb @@ -6,7 +6,6 @@ module Admin before_action :find_broadcast_message, only: [:edit, :update, :destroy] before_action :find_broadcast_messages, only: [:index, :create] - before_action :push_features, only: [:index, :edit] feature_category :onboarding urgency :low @@ -15,8 +14,7 @@ module Admin @broadcast_message = BroadcastMessage.new end - def edit - end + def edit; end def create @broadcast_message = BroadcastMessage.new(broadcast_message_params) @@ -72,7 +70,7 @@ module Admin def preview @broadcast_message = BroadcastMessage.new(broadcast_message_params) - render partial: 'admin/broadcast_messages/preview' + render plain: render_broadcast_message(@broadcast_message), status: :ok end protected @@ -88,18 +86,14 @@ module Admin def broadcast_message_params params.require(:broadcast_message) .permit(%i[ - theme - ends_at - message - starts_at - target_path - broadcast_type - dismissable - ], target_access_levels: []).reverse_merge!(target_access_levels: []) - end - - def push_features - push_frontend_feature_flag(:role_targeted_broadcast_messages, current_user) + theme + ends_at + message + starts_at + target_path + broadcast_type + dismissable + ], target_access_levels: []).reverse_merge!(target_access_levels: []) end end end diff --git a/app/controllers/admin/ci/variables_controller.rb b/app/controllers/admin/ci/variables_controller.rb index ef50d7362c4..4ab67e54766 100644 --- a/app/controllers/admin/ci/variables_controller.rb +++ b/app/controllers/admin/ci/variables_controller.rb @@ -3,7 +3,7 @@ module Admin module Ci class VariablesController < ApplicationController - feature_category :pipeline_authoring + feature_category :secrets_management def show respond_to do |format| @@ -32,10 +32,7 @@ module Admin end def render_instance_variables - render status: :ok, - json: { - variables: ::Ci::InstanceVariableSerializer.new.represent(variables) - } + render status: :ok, json: { variables: ::Ci::InstanceVariableSerializer.new.represent(variables) } end def render_error(errors) diff --git a/app/controllers/admin/cohorts_controller.rb b/app/controllers/admin/cohorts_controller.rb index ce3d769f35e..3948d3635fe 100644 --- a/app/controllers/admin/cohorts_controller.rb +++ b/app/controllers/admin/cohorts_controller.rb @@ -7,7 +7,7 @@ class Admin::CohortsController < Admin::ApplicationController urgency :low - track_custom_event :index, + track_event :index, name: 'i_analytics_cohorts', action: 'perform_analytics_usage_action', label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', diff --git a/app/controllers/admin/dev_ops_report_controller.rb b/app/controllers/admin/dev_ops_report_controller.rb index 71ee19ddf39..2e47dfcb0db 100644 --- a/app/controllers/admin/dev_ops_report_controller.rb +++ b/app/controllers/admin/dev_ops_report_controller.rb @@ -5,7 +5,7 @@ class Admin::DevOpsReportController < Admin::ApplicationController helper_method :show_adoption? - track_custom_event :show, + track_event :show, name: 'i_analytics_dev_ops_score', action: 'perform_analytics_usage_action', label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index e3a33bafb62..0f9ecc60648 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -5,7 +5,7 @@ class Admin::GroupsController < Admin::ApplicationController before_action :group, only: [:edit, :update, :destroy, :project_update, :members_update] - feature_category :subgroups + feature_category :subgroups, [:create, :destroy, :edit, :index, :members_update, :new, :show, :update] def index @groups = groups.sort_by_attribute(@sort = params[:sort]) @@ -65,8 +65,8 @@ class Admin::GroupsController < Admin::ApplicationController Groups::DestroyService.new(@group, current_user).async_execute redirect_to admin_groups_path, - status: :found, - alert: format(_('Group %{group_name} was scheduled for deletion.'), group_name: @group.name) + status: :found, + alert: format(_('Group %{group_name} was scheduled for deletion.'), group_name: @group.name) end private diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index 1dc6c68d8ca..57ef75f12e9 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -3,8 +3,6 @@ class Admin::HooksController < Admin::ApplicationController include ::WebHooks::HookActions - before_action :hook_logs, only: :edit - feature_category :integrations urgency :low, [:test] @@ -26,10 +24,6 @@ class Admin::HooksController < Admin::ApplicationController @hook ||= SystemHook.find(params[:id]) end - def hook_logs - @hook_logs ||= hook.web_hook_logs.recent.page(params[:page]).without_count - end - def hook_param_names %i[enable_ssl_verification token url] end diff --git a/app/controllers/admin/identities_controller.rb b/app/controllers/admin/identities_controller.rb index dcec50e882d..0745ba328c6 100644 --- a/app/controllers/admin/identities_controller.rb +++ b/app/controllers/admin/identities_controller.rb @@ -4,7 +4,7 @@ class Admin::IdentitiesController < Admin::ApplicationController before_action :user before_action :identity, except: [:index, :new, :create] - feature_category :authentication_and_authorization + feature_category :system_access def new @identity = Identity.new diff --git a/app/controllers/admin/impersonation_tokens_controller.rb b/app/controllers/admin/impersonation_tokens_controller.rb index ddc555add5c..dae3337d19b 100644 --- a/app/controllers/admin/impersonation_tokens_controller.rb +++ b/app/controllers/admin/impersonation_tokens_controller.rb @@ -4,7 +4,7 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController before_action :user before_action :verify_impersonation_enabled! - feature_category :authentication_and_authorization + feature_category :user_management def index set_index_vars diff --git a/app/controllers/admin/impersonations_controller.rb b/app/controllers/admin/impersonations_controller.rb index 6c45b03455e..c1a6cb350ec 100644 --- a/app/controllers/admin/impersonations_controller.rb +++ b/app/controllers/admin/impersonations_controller.rb @@ -4,7 +4,7 @@ class Admin::ImpersonationsController < Admin::ApplicationController skip_before_action :authenticate_admin! before_action :authenticate_impersonator! - feature_category :authentication_and_authorization + feature_category :user_management def destroy original_user = stop_impersonation diff --git a/app/controllers/admin/instance_review_controller.rb b/app/controllers/admin/instance_review_controller.rb index cc801bce5b7..87fb1fa1a66 100644 --- a/app/controllers/admin/instance_review_controller.rb +++ b/app/controllers/admin/instance_review_controller.rb @@ -5,7 +5,7 @@ class Admin::InstanceReviewController < Admin::ApplicationController urgency :low def index - redirect_to("#{Gitlab::SubscriptionPortal.subscriptions_instance_review_url}?#{instance_review_params}") + redirect_to("#{subscription_portal_instance_review_url}?#{instance_review_params}") end def instance_review_params diff --git a/app/controllers/admin/keys_controller.rb b/app/controllers/admin/keys_controller.rb index 03383604e30..e4a756ec12d 100644 --- a/app/controllers/admin/keys_controller.rb +++ b/app/controllers/admin/keys_controller.rb @@ -3,7 +3,7 @@ class Admin::KeysController < Admin::ApplicationController before_action :user, only: [:show, :destroy] - feature_category :authentication_and_authorization + feature_category :user_management def show @key = user.keys.find(params[:id]) diff --git a/app/controllers/admin/plan_limits_controller.rb b/app/controllers/admin/plan_limits_controller.rb index ea52198432c..b8608390d4d 100644 --- a/app/controllers/admin/plan_limits_controller.rb +++ b/app/controllers/admin/plan_limits_controller.rb @@ -41,7 +41,6 @@ class Admin::PlanLimitsController < Admin::ApplicationController generic_packages_max_file_size ci_pipeline_size ci_active_jobs - ci_active_pipelines ci_project_subscriptions ci_pipeline_schedules ci_needs_size_limit diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 5d37bd27302..84eb90ce334 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -3,10 +3,10 @@ class Admin::ProjectsController < Admin::ApplicationController include MembersPresentation - before_action :project, only: [:show, :transfer, :repository_check, :destroy] + before_action :project, only: [:show, :transfer, :repository_check, :destroy, :edit, :update] before_action :group, only: [:show, :transfer] - feature_category :projects, [:index, :show, :transfer, :destroy] + feature_category :projects, [:index, :show, :transfer, :destroy, :edit, :update] feature_category :source_code_management, [:repository_check] def index @@ -62,6 +62,22 @@ class Admin::ProjectsController < Admin::ApplicationController end # rubocop: enable CodeReuse/ActiveRecord + def edit; end + + def update + result = ::Projects::UpdateService.new(@project, current_user, project_params).execute + + if result[:status] == :success + unless Gitlab::Utils.to_boolean(project_params['runner_registration_enabled']) + Ci::Runners::ResetRegistrationTokenService.new(@project, current_user).execute + end + + redirect_to [:admin, @project], notice: format(_("Project '%{project_name}' was successfully updated."), project_name: @project.name) + else + render "edit" + end + end + def repository_check RepositoryCheck::SingleRepositoryWorker.perform_async(@project.id) # rubocop:disable CodeReuse/Worker @@ -83,6 +99,18 @@ class Admin::ProjectsController < Admin::ApplicationController def group @group ||= @project.group end + + def project_params + params.require(:project).permit(allowed_project_params) + end + + def allowed_project_params + [ + :description, + :name, + :runner_registration_enabled + ] + end end Admin::ProjectsController.prepend_mod_with('Admin::ProjectsController') diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb index 7dbae565d07..79a31331374 100644 --- a/app/controllers/admin/runner_projects_controller.rb +++ b/app/controllers/admin/runner_projects_controller.rb @@ -10,9 +10,10 @@ class Admin::RunnerProjectsController < Admin::ApplicationController @runner = Ci::Runner.find(params[:runner_project][:runner_id]) if ::Ci::Runners::AssignRunnerService.new(@runner, @project, current_user).execute.success? - redirect_to edit_admin_runner_url(@runner), notice: s_('Runners|Runner assigned to project.') + flash[:success] = s_('Runners|Runner assigned to project.') + redirect_to edit_admin_runner_url(@runner) else - redirect_to edit_admin_runner_url(@runner), alert: 'Failed adding runner to project' + redirect_to edit_admin_runner_url(@runner), alert: s_('Runners|Failed adding runner to project') end end @@ -22,7 +23,8 @@ class Admin::RunnerProjectsController < Admin::ApplicationController ::Ci::Runners::UnassignRunnerService.new(rp, current_user).execute - redirect_to edit_admin_runner_url(runner), status: :found, notice: s_('Runners|Runner unassigned from project.') + flash[:success] = s_('Runners|Runner unassigned from project.') + redirect_to edit_admin_runner_url(runner), status: :found end private diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb index 21a3a0aea0b..f63616a2bea 100644 --- a/app/controllers/admin/runners_controller.rb +++ b/app/controllers/admin/runners_controller.rb @@ -6,7 +6,7 @@ class Admin::RunnersController < Admin::ApplicationController before_action :runner, except: [:index, :new, :tag_list, :runner_setup_scripts] before_action only: [:index] do - push_frontend_feature_flag(:create_runner_workflow, current_user) + push_frontend_feature_flag(:create_runner_workflow_for_admin, current_user) end feature_category :runner @@ -23,7 +23,12 @@ class Admin::RunnersController < Admin::ApplicationController end def new - render_404 unless Feature.enabled?(:create_runner_workflow, current_user) + render_404 unless Feature.enabled?(:create_runner_workflow_for_admin, current_user) + end + + def register + render_404 unless Feature.enabled?(:create_runner_workflow_for_admin, current_user) && + runner.registration_available? end def update diff --git a/app/controllers/admin/sessions_controller.rb b/app/controllers/admin/sessions_controller.rb index 63579421573..bb275532170 100644 --- a/app/controllers/admin/sessions_controller.rb +++ b/app/controllers/admin/sessions_controller.rb @@ -7,7 +7,7 @@ class Admin::SessionsController < ApplicationController before_action :user_is_admin! - feature_category :authentication_and_authorization + feature_category :system_access def new if current_user_mode.admin_mode? diff --git a/app/controllers/admin/spam_logs_controller.rb b/app/controllers/admin/spam_logs_controller.rb index 984ae736697..b27185a6add 100644 --- a/app/controllers/admin/spam_logs_controller.rb +++ b/app/controllers/admin/spam_logs_controller.rb @@ -5,7 +5,7 @@ class Admin::SpamLogsController < Admin::ApplicationController # rubocop: disable CodeReuse/ActiveRecord def index - @spam_logs = SpamLog.includes(:user).order(id: :desc).page(params[:page]) + @spam_logs = SpamLog.includes(:user).order(id: :desc).page(params[:page]).without_count end # rubocop: enable CodeReuse/ActiveRecord @@ -15,8 +15,8 @@ class Admin::SpamLogsController < Admin::ApplicationController if params[:remove_user] spam_log.remove_user(deleted_by: current_user) redirect_to admin_spam_logs_path, - status: :found, - notice: format(_('User %{username} was successfully removed.'), username: spam_log.user.username) + status: :found, + notice: format(_('User %{username} was successfully removed.'), username: spam_log.user.username) else spam_log.destroy head :ok diff --git a/app/controllers/admin/topics_controller.rb b/app/controllers/admin/topics_controller.rb index 345a778772d..94d084932ad 100644 --- a/app/controllers/admin/topics_controller.rb +++ b/app/controllers/admin/topics_controller.rb @@ -41,8 +41,8 @@ class Admin::TopicsController < Admin::ApplicationController @topic.destroy! redirect_to admin_topics_path, - status: :found, - notice: format(_('Topic %{topic_name} was successfully removed.'), topic_name: @topic.title_or_name) + status: :found, + notice: format(_('Topic %{topic_name} was successfully removed.'), topic_name: @topic.title_or_name) end def merge diff --git a/app/controllers/admin/usage_trends_controller.rb b/app/controllers/admin/usage_trends_controller.rb index 082b38ac3a8..f88028535c1 100644 --- a/app/controllers/admin/usage_trends_controller.rb +++ b/app/controllers/admin/usage_trends_controller.rb @@ -3,7 +3,7 @@ class Admin::UsageTrendsController < Admin::ApplicationController include ProductAnalyticsTracking - track_custom_event :index, + track_event :index, name: 'i_analytics_instance_statistics', action: 'perform_analytics_usage_action', label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 00b17bf381f..45a7901b2c4 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -96,28 +96,29 @@ class Admin::UsersController < Admin::ApplicationController end def deactivate - if user.blocked? - return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user cannot be deactivated")) - end - - return redirect_back_or_admin_user(notice: _("Successfully deactivated")) if user.deactivated? - return redirect_back_or_admin_user(notice: _("Internal users cannot be deactivated")) if user.internal? + deactivate_service = Users::DeactivateService.new(current_user, skip_authorization: true) + result = deactivate_service.execute(user) - unless user.can_be_deactivated? - return redirect_back_or_admin_user(notice: format(_("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated"), minimum_inactive_days: Gitlab::CurrentSettings.deactivate_dormant_users_period)) + if result.success? + redirect_back_or_admin_user(notice: _("Successfully deactivated")) + else + redirect_back_or_admin_user(alert: result.message) end - - user.deactivate - redirect_back_or_admin_user(notice: _("Successfully deactivated")) end def block result = Users::BlockService.new(current_user).execute(user) - if result[:status] == :success - redirect_back_or_admin_user(notice: _("Successfully blocked")) - else - redirect_back_or_admin_user(alert: _("Error occurred. User was not blocked")) + respond_to do |format| + if result[:status] == :success + notice = _("Successfully blocked") + format.json { render json: { notice: notice } } + format.html { redirect_back_or_admin_user(notice: notice) } + else + alert = _("Error occurred. User was not blocked") + format.json { render json: { error: alert } } + format.html { redirect_back_or_admin_user(alert: alert) } + end end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 353f9098b95..9749af08dca 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -29,11 +29,9 @@ class ApplicationController < ActionController::Base before_action :limit_session_time, if: -> { !current_user } before_action :authenticate_user!, except: [:route_not_found] before_action :enforce_terms!, if: :should_enforce_terms? - before_action :validate_user_service_ticket! before_action :check_password_expiration, if: :html_request? before_action :ldap_security_check before_action :default_headers - before_action :default_cache_headers before_action :add_gon_variables, if: :html_request? before_action :configure_permitted_parameters, if: :devise_controller? before_action :require_email, unless: :devise_controller? @@ -61,12 +59,10 @@ class ApplicationController < ActionController::Base helper_method :can? helper_method :import_sources_enabled?, :github_import_enabled?, :gitea_import_enabled?, :github_import_configured?, - :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :bitbucket_server_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled?, - :manifest_import_enabled?, :phabricator_import_enabled?, - :masked_page_url + :manifest_import_enabled?, :masked_page_url def self.endpoint_id_for_action(action_name) "#{name}##{action_name}" @@ -90,7 +86,7 @@ class ApplicationController < ActionController::Base render_403 end - rescue_from Gitlab::Auth::IpBlacklisted do + rescue_from Gitlab::Auth::IpBlocked do Gitlab::AuthLogger.error( message: 'Rack_Attack', env: :blocklist, @@ -112,6 +108,11 @@ class ApplicationController < ActionController::Base render plain: e.message, status: :too_many_requests end + rescue_from Gitlab::Git::ResourceExhaustedError do |e| + response.headers.merge!(e.headers) + render plain: e.message, status: :too_many_requests + end + content_security_policy do |p| next if p.directives.blank? next unless Gitlab::CurrentSettings.snowplow_enabled? && !Gitlab::CurrentSettings.snowplow_collector_hostname.blank? @@ -260,10 +261,7 @@ class ApplicationController < ActionController::Base respond_to do |format| format.html do - render template, - layout: "errors", - status: status, - locals: { message: message } + render template, layout: "errors", status: status, locals: { message: message } end format.any { head status } end @@ -319,10 +317,6 @@ class ApplicationController < ActionController::Base headers['X-Content-Type-Options'] = 'nosniff' end - def default_cache_headers - headers['Pragma'] = 'no-cache' # HTTP 1.0 compatibility - end - def stream_csv_headers(csv_filename) no_cache_headers stream_headers @@ -331,20 +325,6 @@ class ApplicationController < ActionController::Base headers['Content-Disposition'] = "attachment; filename=\"#{csv_filename}\"" end - def validate_user_service_ticket! - return unless signed_in? && session[:service_tickets] - - valid = session[:service_tickets].all? do |provider, ticket| - Gitlab::Auth::OAuth::Session.valid?(provider, ticket) - end - - unless valid - session[:service_tickets] = nil - sign_out current_user - redirect_to new_user_session_path - end - end - def check_password_expiration return if session[:impersonator_id] || !current_user&.allow_password_authentication? @@ -452,14 +432,6 @@ class ApplicationController < ActionController::Base Gitlab::Auth::OAuth::Provider.enabled?(:github) end - def gitlab_import_enabled? - request.host != 'gitlab.com' && Gitlab::CurrentSettings.import_sources.include?('gitlab') - end - - def gitlab_import_configured? - Gitlab::Auth::OAuth::Provider.enabled?(:gitlab) - end - def bitbucket_import_enabled? Gitlab::CurrentSettings.import_sources.include?('bitbucket') end @@ -484,10 +456,6 @@ class ApplicationController < ActionController::Base Gitlab::CurrentSettings.import_sources.include?('manifest') end - def phabricator_import_enabled? - Gitlab::PhabricatorImport.available? - end - # U2F (universal 2nd factor) devices need a unique identifier for the application # to perform authentication. # https://developers.yubico.com/U2F/App_ID.html diff --git a/app/controllers/chaos_controller.rb b/app/controllers/chaos_controller.rb index 6139168d29f..7328b793b09 100644 --- a/app/controllers/chaos_controller.rb +++ b/app/controllers/chaos_controller.rb @@ -52,13 +52,14 @@ class ChaosController < ActionController::Base def validate_chaos_secret unless chaos_secret_configured render plain: "chaos misconfigured: please configure GITLAB_CHAOS_SECRET", - status: :internal_server_error + status: :internal_server_error + return end unless Devise.secure_compare(chaos_secret_configured, chaos_secret_request) render plain: "To experience chaos, please set a valid `X-Chaos-Secret` header or `token` param", - status: :unauthorized + status: :unauthorized end end diff --git a/app/controllers/clusters/base_controller.rb b/app/controllers/clusters/base_controller.rb index 2401d8b1044..dd5be596ad1 100644 --- a/app/controllers/clusters/base_controller.rb +++ b/app/controllers/clusters/base_controller.rb @@ -8,7 +8,7 @@ class Clusters::BaseController < ApplicationController helper_method :clusterable - feature_category :kubernetes_management + feature_category :deployment_management urgency :low, [ :index, :show, :environments, :cluster_status, :prometheus_proxy, :destroy, :new_cluster_docs, :connect, :new, :create_user diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb index 51150700860..873aa5e18dc 100644 --- a/app/controllers/clusters/clusters_controller.rb +++ b/app/controllers/clusters/clusters_controller.rb @@ -10,7 +10,6 @@ class Clusters::ClustersController < Clusters::BaseController before_action :authorize_read_cluster!, only: [:show, :index] before_action :authorize_create_cluster!, only: [:connect] before_action :authorize_update_cluster!, only: [:update] - before_action :update_applications_status, only: [:cluster_status] before_action :ensure_feature_enabled!, except: [:index, :new_cluster_docs] helper_method :token_in_session @@ -223,10 +222,6 @@ class Clusters::ClustersController < Clusters::BaseController @expires_at_in_session ||= session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at] end - - def update_applications_status - @cluster.applications.each(&:schedule_status_update) - end end Clusters::ClustersController.prepend_mod_with('Clusters::ClustersController') diff --git a/app/controllers/concerns/access_tokens_actions.rb b/app/controllers/concerns/access_tokens_actions.rb index 6a84c436aae..84cbdda1581 100644 --- a/app/controllers/concerns/access_tokens_actions.rb +++ b/app/controllers/concerns/access_tokens_actions.rb @@ -68,7 +68,7 @@ module AccessTokensActions # user in the resource without multiple queries. resource.members.load - @scopes = Gitlab::Auth.resource_bot_scopes + @scopes = Gitlab::Auth.available_scopes_for(resource) @active_access_tokens = active_access_tokens end # rubocop:enable Gitlab/ModuleWithInstanceVariables diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb index b4a36b7db22..9fd86e6a7e0 100644 --- a/app/controllers/concerns/authenticates_with_two_factor.rb +++ b/app/controllers/concerns/authenticates_with_two_factor.rb @@ -25,13 +25,7 @@ module AuthenticatesWithTwoFactor session[:user_password_hash] = Digest::SHA256.hexdigest(user.encrypted_password) add_gon_variables - push_frontend_feature_flag(:webauthn) - - if Feature.enabled?(:webauthn) - setup_webauthn_authentication(user) - else - setup_u2f_authentication(user) - end + setup_webauthn_authentication(user) render 'devise/sessions/two_factor' end @@ -54,11 +48,7 @@ module AuthenticatesWithTwoFactor if user_params[:otp_attempt].present? && session[:otp_user_id] authenticate_with_two_factor_via_otp(user) elsif user_params[:device_response].present? && session[:otp_user_id] - if user.two_factor_webauthn_enabled? - authenticate_with_two_factor_via_webauthn(user) - else - authenticate_with_two_factor_via_u2f(user) - end + authenticate_with_two_factor_via_webauthn(user) elsif user && user.valid_password?(user_params[:password]) prompt_for_two_factor(user) end @@ -96,15 +86,6 @@ module AuthenticatesWithTwoFactor end end - # Authenticate using the response from a U2F (universal 2nd factor) device - def authenticate_with_two_factor_via_u2f(user) - if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenge]) - handle_two_factor_success(user) - else - handle_two_factor_failure(user, 'U2F', _('Authentication via U2F device failed.')) - end - end - def authenticate_with_two_factor_via_webauthn(user) if Webauthn::AuthenticateService.new(user, user_params[:device_response], session[:challenge]).execute handle_two_factor_success(user) @@ -113,31 +94,17 @@ module AuthenticatesWithTwoFactor end end - # Setup in preparation of communication with a U2F (universal 2nd factor) device - # Actual communication is performed using a Javascript API # rubocop: disable CodeReuse/ActiveRecord - def setup_u2f_authentication(user) - key_handles = user.u2f_registrations.pluck(:key_handle) - u2f = U2F::U2F.new(u2f_app_id) - - if key_handles.present? - sign_requests = u2f.authentication_requests(key_handles) - session[:challenge] ||= u2f.challenge - gon.push(u2f: { challenge: session[:challenge], app_id: u2f_app_id, - sign_requests: sign_requests }) - end - end - def setup_webauthn_authentication(user) if user.webauthn_registrations.present? webauthn_registration_ids = user.webauthn_registrations.pluck(:credential_xid) - get_options = WebAuthn::Credential.options_for_get(allow: webauthn_registration_ids, - user_verification: 'discouraged', - extensions: { appid: WebAuthn.configuration.origin }) - - session[:credentialRequestOptions] = get_options + get_options = WebAuthn::Credential.options_for_get( + allow: webauthn_registration_ids, + user_verification: 'discouraged', + extensions: { appid: WebAuthn.configuration.origin } + ) session[:challenge] = get_options.challenge gon.push(webauthn: { options: Gitlab::Json.dump(get_options) }) end diff --git a/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb b/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb index 574fc6c0f37..045ccf1e5b8 100644 --- a/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb +++ b/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb @@ -11,13 +11,7 @@ module AuthenticatesWithTwoFactorForAdminMode return handle_locked_user(user) unless user.can?(:log_in) session[:otp_user_id] = user.id - push_frontend_feature_flag(:webauthn) - - if user.two_factor_webauthn_enabled? - setup_webauthn_authentication(user) - else - setup_u2f_authentication(user) - end + setup_webauthn_authentication(user) render 'admin/sessions/two_factor', layout: 'application' end @@ -30,11 +24,7 @@ module AuthenticatesWithTwoFactorForAdminMode if user_params[:otp_attempt].present? && session[:otp_user_id] admin_mode_authenticate_with_two_factor_via_otp(user) elsif user_params[:device_response].present? && session[:otp_user_id] - if user.two_factor_webauthn_enabled? - admin_mode_authenticate_with_two_factor_via_webauthn(user) - else - admin_mode_authenticate_with_two_factor_via_u2f(user) - end + admin_mode_authenticate_with_two_factor_via_webauthn(user) elsif user && user.valid_password?(user_params[:password]) admin_mode_prompt_for_two_factor(user) else @@ -56,14 +46,6 @@ module AuthenticatesWithTwoFactorForAdminMode end end - def admin_mode_authenticate_with_two_factor_via_u2f(user) - if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenge]) - admin_handle_two_factor_success - else - admin_handle_two_factor_failure(user, 'U2F', _('Authentication via U2F device failed.')) - end - end - def admin_mode_authenticate_with_two_factor_via_webauthn(user) if Webauthn::AuthenticateService.new(user, user_params[:device_response], session[:challenge]).execute admin_handle_two_factor_success diff --git a/app/controllers/concerns/confirm_email_warning.rb b/app/controllers/concerns/confirm_email_warning.rb index 2711c823275..2efea461a35 100644 --- a/app/controllers/concerns/confirm_email_warning.rb +++ b/app/controllers/concerns/confirm_email_warning.rb @@ -11,7 +11,7 @@ module ConfirmEmailWarning protected def show_confirm_warning? - html_request? && request.get? && Feature.enabled?(:soft_email_confirmation) + html_request? && request.get? && Gitlab::CurrentSettings.email_confirmation_setting_soft? end def set_confirm_warning diff --git a/app/controllers/concerns/cycle_analytics_params.rb b/app/controllers/concerns/cycle_analytics_params.rb index 5199d879595..87fa9772e75 100644 --- a/app/controllers/concerns/cycle_analytics_params.rb +++ b/app/controllers/concerns/cycle_analytics_params.rb @@ -19,7 +19,6 @@ module CycleAnalyticsParams @options ||= {}.tap do |opts| opts[:current_user] = current_user opts[:projects] = params[:project_ids] if params[:project_ids] - opts[:group] = params[:group_id] if params[:group_id] opts[:from] = params[:from] || start_date(params) opts[:to] = params[:to] if params[:to] opts[:end_event_filter] = params[:end_event_filter] if params[:end_event_filter] @@ -62,7 +61,7 @@ module CycleAnalyticsParams end def all_cycle_analytics_params - permitted_cycle_analytics_params.merge(current_user: current_user) + permitted_cycle_analytics_params.merge(current_user: current_user, namespace: namespace) end def request_params @@ -78,5 +77,3 @@ module CycleAnalyticsParams end end end - -CycleAnalyticsParams.prepend_mod_with('CycleAnalyticsParams') diff --git a/app/controllers/concerns/enforces_two_factor_authentication.rb b/app/controllers/concerns/enforces_two_factor_authentication.rb index cdef1a45a27..8068913eea2 100644 --- a/app/controllers/concerns/enforces_two_factor_authentication.rb +++ b/app/controllers/concerns/enforces_two_factor_authentication.rb @@ -27,7 +27,8 @@ module EnforcesTwoFactorAuthentication render_error( format( _("Authentication error: enable 2FA in your profile settings to continue using GitLab: %{mfa_help_page}"), - mfa_help_page: mfa_help_page_url), + mfa_help_page: mfa_help_page_url + ), status: :unauthorized ) else diff --git a/app/controllers/concerns/integrations/actions.rb b/app/controllers/concerns/integrations/actions.rb index 7bebafae0fd..c0816c2fe9c 100644 --- a/app/controllers/concerns/integrations/actions.rb +++ b/app/controllers/concerns/integrations/actions.rb @@ -8,6 +8,9 @@ module Integrations::Actions include IntegrationsHelper before_action :integration, only: [:edit, :update, :overrides, :test] + before_action :render_404, only: :edit, if: -> do + integration.to_param == 'prometheus' && Feature.enabled?(:remove_monitor_metrics) + end urgency :low, [:test] end diff --git a/app/controllers/concerns/integrations/params.rb b/app/controllers/concerns/integrations/params.rb index 4d181ded071..af984776828 100644 --- a/app/controllers/concerns/integrations/params.rb +++ b/app/controllers/concerns/integrations/params.rb @@ -8,6 +8,7 @@ module Integrations :app_store_issuer_id, :app_store_key_id, :app_store_private_key, + :app_store_private_key_file_name, :active, :alert_events, :api_key, @@ -52,6 +53,9 @@ module Integrations :issues_events, :issues_url, :jenkins_url, + :jira_auth_type, + :jira_issue_prefix, + :jira_issue_regex, :jira_issue_transition_automatic, :jira_issue_transition_id, :manual_configuration, @@ -60,6 +64,7 @@ module Integrations :namespace, :new_issue_url, :notify_only_broken_pipelines, + :package_name, :password, :priority, :project_key, @@ -72,6 +77,8 @@ module Integrations :server, :server_host, :server_port, + :service_account_key, + :service_account_key_file_name, :sound, :subdomain, :teamcity_url, diff --git a/app/controllers/concerns/invisible_captcha_on_signup.rb b/app/controllers/concerns/invisible_captcha_on_signup.rb index b78869e02d0..a704ff251b3 100644 --- a/app/controllers/concerns/invisible_captcha_on_signup.rb +++ b/app/controllers/concerns/invisible_captcha_on_signup.rb @@ -26,15 +26,17 @@ module InvisibleCaptchaOnSignup end def invisible_captcha_honeypot_counter - @invisible_captcha_honeypot_counter ||= - Gitlab::Metrics.counter(:bot_blocked_by_invisible_captcha_honeypot, - 'Counter of blocked sign up attempts with filled honeypot') + @invisible_captcha_honeypot_counter ||= Gitlab::Metrics.counter( + :bot_blocked_by_invisible_captcha_honeypot, + 'Counter of blocked sign up attempts with filled honeypot' + ) end def invisible_captcha_timestamp_counter - @invisible_captcha_timestamp_counter ||= - Gitlab::Metrics.counter(:bot_blocked_by_invisible_captcha_timestamp, - 'Counter of blocked sign up attempts with invalid timestamp') + @invisible_captcha_timestamp_counter ||= Gitlab::Metrics.counter( + :bot_blocked_by_invisible_captcha_timestamp, + 'Counter of blocked sign up attempts with invalid timestamp' + ) end def log_request(message) diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb index e1381b4173f..0ad8a08960a 100644 --- a/app/controllers/concerns/issuable_actions.rb +++ b/app/controllers/concerns/issuable_actions.rb @@ -76,7 +76,7 @@ module IssuableActions title_text: issuable.title, description: view_context.markdown_field(issuable, :description), description_text: issuable.description, - task_status: issuable.task_status, + task_completion_status: issuable.task_completion_status, lock_version: issuable.lock_version } @@ -97,7 +97,7 @@ module IssuableActions index_path = polymorphic_path([parent, issuable.class]) respond_to do |format| - format.html { redirect_to index_path } + format.html { redirect_to index_path, status: :see_other } format.json do render json: { web_url: index_path @@ -151,9 +151,7 @@ module IssuableActions end case issuable - when MergeRequest - render_mr_discussions(discussion_notes, discussion_serializer, discussion_cache_context) - when Issue + when MergeRequest, Issue if stale?(etag: [discussion_cache_context, discussion_notes]) render json: discussion_serializer.represent(discussion_notes, context: self) end @@ -164,23 +162,6 @@ module IssuableActions private - def render_mr_discussions(discussions, serializer, cache_context) - return unless stale?(etag: [cache_context, discussions]) - - if Feature.enabled?(:disabled_mr_discussions_redis_cache, project) - render json: serializer.represent(discussions, context: self) - else - render_cached_discussions(discussions, serializer, cache_context) - end - end - - def render_cached_discussions(discussions, serializer, cache_context) - render_cached(discussions, - with: serializer, - cache_context: ->(_) { cache_context }, - context: self) - end - def notes_filter strong_memoize(:notes_filter) do notes_filter_param = params[:notes_filter]&.to_i @@ -209,7 +190,7 @@ module IssuableActions end def discussion_cache_context - [current_user&.cache_key, project.team.human_max_access(current_user&.id)].join(':') + [current_user&.cache_key, project.team.human_max_access(current_user&.id), 'v2'].join(':') end def discussion_serializer diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index a202808e2c3..b02a636ff74 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -141,7 +141,7 @@ module IssuableCollections common_attributes = [:author, :assignees, :labels, :milestone] @preload_for_collection ||= case collection_type when 'Issue' - common_attributes + [:project, project: :namespace] + common_attributes + [:work_item_type, :project, project: :namespace] when 'MergeRequest' common_attributes + [ :target_project, :latest_merge_request_diff, :approvals, :approved_by_users, :reviewers, diff --git a/app/controllers/concerns/issuable_collections_action.rb b/app/controllers/concerns/issuable_collections_action.rb index 31445eb3eca..86ad87cc2c1 100644 --- a/app/controllers/concerns/issuable_collections_action.rb +++ b/app/controllers/concerns/issuable_collections_action.rb @@ -48,8 +48,6 @@ module IssuableCollectionsAction Issue::SORTING_PREFERENCE_FIELD when 'merge_requests' MergeRequest::SORTING_PREFERENCE_FIELD - else - nil end end @@ -59,8 +57,6 @@ module IssuableCollectionsAction IssuesFinder when 'merge_requests' MergeRequestsFinder - else - nil end end diff --git a/app/controllers/concerns/kas_cookie.rb b/app/controllers/concerns/kas_cookie.rb new file mode 100644 index 00000000000..c66bf7c9e8c --- /dev/null +++ b/app/controllers/concerns/kas_cookie.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module KasCookie + extend ActiveSupport::Concern + + included do + content_security_policy_with_context do |p| + next unless ::Gitlab::Kas::UserAccess.enabled? + + kas_url = ::Gitlab::Kas.tunnel_url + next if URI(kas_url).host == ::Gitlab.config.gitlab.host # already allowed, no need for exception + + kas_url += '/' unless kas_url.end_with?('/') + p.connect_src(*Array.wrap(p.directives['connect-src']), kas_url) + end + end + + def set_kas_cookie + return unless ::Gitlab::Kas::UserAccess.enabled? + + public_session_id = Gitlab::Session.current&.id&.public_id + return unless public_session_id + + cookie_data = ::Gitlab::Kas::UserAccess.cookie_data(public_session_id) + + cookies[::Gitlab::Kas::COOKIE_KEY] = cookie_data + end +end diff --git a/app/controllers/concerns/known_sign_in.rb b/app/controllers/concerns/known_sign_in.rb index cacc7e4628f..997f26fa959 100644 --- a/app/controllers/concerns/known_sign_in.rb +++ b/app/controllers/concerns/known_sign_in.rb @@ -26,8 +26,13 @@ module KnownSignIn end def update_cookie - set_secure_cookie(KNOWN_SIGN_IN_COOKIE, current_user.id, - type: COOKIE_TYPE_ENCRYPTED, httponly: true, expires: KNOWN_SIGN_IN_COOKIE_EXPIRY) + set_secure_cookie( + KNOWN_SIGN_IN_COOKIE, + current_user.id, + type: COOKIE_TYPE_ENCRYPTED, + httponly: true, + expires: KNOWN_SIGN_IN_COOKIE_EXPIRY + ) end def sessions diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb index 773e4c15d6e..09b82e36b1a 100644 --- a/app/controllers/concerns/membership_actions.rb +++ b/app/controllers/concerns/membership_actions.rb @@ -51,7 +51,7 @@ module MembershipActions _("User was successfully removed from project.") end - redirect_to members_page_url, notice: message + redirect_to members_page_url, notice: message, status: :see_other end format.js { head :ok } @@ -63,10 +63,10 @@ module MembershipActions if access_requester.persisted? redirect_to polymorphic_path(membershipable), - notice: _('Your request for access has been queued for review.') + notice: _('Your request for access has been queued for review.') else redirect_to polymorphic_path(membershipable), - alert: format(_("Your request for access could not be processed: %{error_message}"), error_message: access_requester.errors.full_messages.to_sentence) + alert: format(_("Your request for access could not be processed: %{error_message}"), error_message: access_requester.errors.full_messages.to_sentence) end end diff --git a/app/controllers/concerns/metrics_dashboard.rb b/app/controllers/concerns/metrics_dashboard.rb index 338c3af235b..7e202235cfa 100644 --- a/app/controllers/concerns/metrics_dashboard.rb +++ b/app/controllers/concerns/metrics_dashboard.rb @@ -79,7 +79,7 @@ module MetricsDashboard end def starred_dashboards - @starred_dashboards ||= begin + @starred_dashboards ||= if project_for_dashboard.present? ::Metrics::UsersStarredDashboardsFinder .new(user: current_user, project: project_for_dashboard) @@ -89,7 +89,6 @@ module MetricsDashboard else Set.new end - end end # Project is not defined for group and admin level clusters. diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb index 512dbf0de5d..06b9c901e4a 100644 --- a/app/controllers/concerns/notes_actions.rb +++ b/app/controllers/concerns/notes_actions.rb @@ -45,7 +45,8 @@ module NotesActions respond_to do |format| format.json do json = { - commands_changes: @note.commands_changes&.slice(:emoji_award, :time_estimate, :spend_time) + commands_changes: @note.commands_changes&.slice(:emoji_award, :time_estimate, :spend_time), + command_names: @note.command_names } if @note.persisted? && return_discussion? diff --git a/app/controllers/concerns/observability/content_security_policy.rb b/app/controllers/concerns/observability/content_security_policy.rb index 3865e3b606d..1e25dc492a0 100644 --- a/app/controllers/concerns/observability/content_security_policy.rb +++ b/app/controllers/concerns/observability/content_security_policy.rb @@ -12,17 +12,17 @@ module Observability defined?(project) ? project&.group : nil end - next if p.directives.blank? || !Gitlab::Observability.observability_enabled?(current_user, current_group) + next if p.directives.blank? || !Feature.enabled?(:observability_group_tab, current_group) default_frame_src = p.directives['frame-src'] || p.directives['default-src'] # When ObservabilityUI is not authenticated, it needs to be able # to redirect to the GL sign-in page, hence '/users/sign_in' and '/oauth/authorize' - frame_src_values = Array.wrap(default_frame_src) | [Gitlab::Observability.observability_url, - Gitlab::Utils.append_path(Gitlab.config.gitlab.url, -'/users/sign_in'), - Gitlab::Utils.append_path(Gitlab.config.gitlab.url, -'/oauth/authorize')] + frame_src_values = Array.wrap(default_frame_src) | [ + Gitlab::Observability.observability_url, + Gitlab::Utils.append_path(Gitlab.config.gitlab.url, '/users/sign_in'), + Gitlab::Utils.append_path(Gitlab.config.gitlab.url, '/oauth/authorize') + ] p.frame_src(*frame_src_values) end diff --git a/app/controllers/concerns/product_analytics_tracking.rb b/app/controllers/concerns/product_analytics_tracking.rb index 5696e441ad0..5424354b92c 100644 --- a/app/controllers/concerns/product_analytics_tracking.rb +++ b/app/controllers/concerns/product_analytics_tracking.rb @@ -2,51 +2,26 @@ module ProductAnalyticsTracking include Gitlab::Tracking::Helpers - include RedisTracking extend ActiveSupport::Concern - MIGRATED_EVENTS = ['g_analytics_valuestream'].freeze - class_methods do - # TODO: Remove once all the events are migrated to #track_custom_event - # during https://gitlab.com/groups/gitlab-org/-/epics/8641 - def track_event(*controller_actions, name:, conditions: nil, destinations: [:redis_hll], &block) - custom_conditions = [:trackable_html_request?, *conditions] - - after_action only: controller_actions, if: custom_conditions do - route_events_to(destinations, name, &block) - end - end - - def track_custom_event(*controller_actions, name:, action:, label:, conditions: nil, destinations: [:redis_hll], &block) + def track_event(*controller_actions, name:, action: nil, label: nil, conditions: nil, destinations: [:redis_hll], &block) custom_conditions = [:trackable_html_request?, *conditions] after_action only: controller_actions, if: custom_conditions do - route_custom_events_to(destinations, name, action, label, &block) + route_events_to(destinations, name, action, label, &block) end end end private - def route_events_to(destinations, name, &block) - track_unique_redis_hll_event(name, &block) if destinations.include?(:redis_hll) - - return unless destinations.include?(:snowplow) && event_enabled?(name) - - Gitlab::Tracking.event( - self.class.to_s, - name, - namespace: tracking_namespace_source, - user: current_user, - context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: name).to_context] - ) - end - - def route_custom_events_to(destinations, name, action, label, &block) + def route_events_to(destinations, name, action, label, &block) track_unique_redis_hll_event(name, &block) if destinations.include?(:redis_hll) - return unless destinations.include?(:snowplow) && event_enabled?(name) + return unless destinations.include?(:snowplow) + raise "action is required when destination is snowplow" unless action + raise "label is required when destination is snowplow" unless label optional_arguments = { namespace: tracking_namespace_source, @@ -64,34 +39,22 @@ module ProductAnalyticsTracking ) end - def event_enabled?(event) - return true if MIGRATED_EVENTS.include?(event) + def track_unique_redis_hll_event(event_name, &block) + custom_id = block ? yield(self) : nil + + unique_id = custom_id || visitor_id - events_to_ff = { - i_search_paid: :_phase2, - i_search_total: :_phase2, - i_search_advanced: :_phase2, - i_ecosystem_jira_service_list_issues: :_phase2, - users_viewing_analytics_group_devops_adoption: :_phase2, - i_analytics_dev_ops_adoption: :_phase2, - i_analytics_dev_ops_score: :_phase2, - p_analytics_merge_request: :_phase2, - i_analytics_instance_statistics: :_phase2, - g_analytics_contribution: :_phase2, - p_analytics_pipelines: :_phase2, - p_analytics_code_reviews: :_phase2, - p_analytics_valuestream: :_phase2, - p_analytics_insights: :_phase2, - p_analytics_issues: :_phase2, - p_analytics_repo: :_phase2, - g_analytics_insights: :_phase2, - g_analytics_issues: :_phase2, - g_analytics_productivity: :_phase2, - i_analytics_cohorts: :_phase2, + return unless unique_id + + Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event_name, values: unique_id) + end - g_compliance_dashboard: :_phase4 - } + def visitor_id + return cookies[:visitor_id] if cookies[:visitor_id].present? + return unless current_user - Feature.enabled?("route_hll_to_snowplow#{events_to_ff[event.to_sym]}", tracking_namespace_source) + uuid = SecureRandom.uuid + cookies[:visitor_id] = { value: uuid, expires: 24.months } + uuid end end diff --git a/app/controllers/concerns/redis_tracking.rb b/app/controllers/concerns/redis_tracking.rb deleted file mode 100644 index 445e72b8266..00000000000 --- a/app/controllers/concerns/redis_tracking.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -# Example: -# -# # In controller include module -# # Track event for index action -# -# include RedisTracking -# -# track_redis_hll_event :index, :show, name: 'i_analytics_dev_ops_score' -# -# You can also pass custom conditions using `if:`, using the same format as with Rails callbacks. -# You can also pass an optional block that calculates and returns a custom id to track. -module RedisTracking - include Gitlab::Tracking::Helpers - extend ActiveSupport::Concern - - class_methods do - def track_redis_hll_event(*controller_actions, name:, if: nil, &block) - custom_conditions = Array.wrap(binding.local_variable_get('if')) - conditions = [:trackable_html_request?, *custom_conditions] - - after_action only: controller_actions, if: conditions do - track_unique_redis_hll_event(name, &block) - end - end - end - - private - - def track_unique_redis_hll_event(event_name, &block) - custom_id = block ? yield(self) : nil - - unique_id = custom_id || visitor_id - - return unless unique_id - - Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event_name, values: unique_id) - end - - def visitor_id - return cookies[:visitor_id] if cookies[:visitor_id].present? - return unless current_user - - uuid = SecureRandom.uuid - cookies[:visitor_id] = { value: uuid, expires: 24.months } - uuid - end -end diff --git a/app/controllers/concerns/registrations_tracking.rb b/app/controllers/concerns/registrations_tracking.rb deleted file mode 100644 index 14743349c1a..00000000000 --- a/app/controllers/concerns/registrations_tracking.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -module RegistrationsTracking - extend ActiveSupport::Concern - - included do - helper_method :glm_tracking_params - end - - private - - def glm_tracking_params - params.permit(:glm_source, :glm_content) - end -end diff --git a/app/controllers/concerns/renders_commits.rb b/app/controllers/concerns/renders_commits.rb index f1f5a1179c9..fca394f9fe1 100644 --- a/app/controllers/concerns/renders_commits.rb +++ b/app/controllers/concerns/renders_commits.rb @@ -33,6 +33,6 @@ module RendersCommits def valid_ref?(ref_name) return true unless ref_name.present? - Gitlab::GitRefValidator.validate(ref_name) + Gitlab::GitRefValidator.validate(ref_name, skip_head_ref_check: true) end end diff --git a/app/controllers/concerns/renders_member_access.rb b/app/controllers/concerns/renders_member_access.rb index 745830181c1..133d797c8ac 100644 --- a/app/controllers/concerns/renders_member_access.rb +++ b/app/controllers/concerns/renders_member_access.rb @@ -15,7 +15,8 @@ module RendersMemberAccess method_name = "max_member_access_for_#{klass.name.underscore}_ids" - current_user.public_send(method_name, collection.ids) # rubocop:disable GitlabSecurity/PublicSend + collection_ids = collection.try(:map, &:id) || collection.ids + current_user.public_send(method_name, collection_ids) # rubocop:disable GitlabSecurity/PublicSend end # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/controllers/concerns/renders_notes.rb b/app/controllers/concerns/renders_notes.rb index f8e3717acee..889d3f0a9d2 100644 --- a/app/controllers/concerns/renders_notes.rb +++ b/app/controllers/concerns/renders_notes.rb @@ -24,13 +24,13 @@ module RendersNotes # rubocop: disable CodeReuse/ActiveRecord def preload_noteable_for_regular_notes(notes) - ActiveRecord::Associations::Preloader.new.preload(notes.reject(&:for_commit?), :noteable) + ActiveRecord::Associations::Preloader.new(records: notes.reject(&:for_commit?), associations: :noteable).call end # rubocop: enable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord def preload_author_status(notes) - ActiveRecord::Associations::Preloader.new.preload(notes, { author: :status }) + ActiveRecord::Associations::Preloader.new(records: notes, associations: { author: :status }).call end # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/controllers/concerns/renders_projects_list.rb b/app/controllers/concerns/renders_projects_list.rb index 05bd9972ee7..2d37bc3f9a5 100644 --- a/app/controllers/concerns/renders_projects_list.rb +++ b/app/controllers/concerns/renders_projects_list.rb @@ -1,13 +1,17 @@ # frozen_string_literal: true module RendersProjectsList + include RendersMemberAccess + def prepare_projects_for_rendering(projects) preload_max_member_access_for_collection(Project, projects) + current_user.preloaded_member_roles_for_projects(projects) if current_user # Call the count methods on every project, so the BatchLoader would load them all at # once when the entities are rendered projects.each(&:forks_count) projects.each(&:open_issues_count) + projects.each(&:open_merge_requests_count) projects end diff --git a/app/controllers/concerns/search_rate_limitable.rb b/app/controllers/concerns/search_rate_limitable.rb index a77ebd276b6..7cce30dbb3c 100644 --- a/app/controllers/concerns/search_rate_limitable.rb +++ b/app/controllers/concerns/search_rate_limitable.rb @@ -7,9 +7,25 @@ module SearchRateLimitable def check_search_rate_limit! if current_user - check_rate_limit!(:search_rate_limit, scope: [current_user]) + # Because every search in the UI typically runs concurrent searches with different + # scopes to get counts, we apply rate limits on the search scope if it is present. + # + # If abusive search is detected, we have stricter limits and ignore the search scope. + check_rate_limit!(:search_rate_limit, scope: [current_user, safe_search_scope].compact) else check_rate_limit!(:search_rate_limit_unauthenticated, scope: [request.ip]) end end + + def safe_search_scope + # Sometimes search scope can have abusive length or invalid keyword. We don't want + # to send those to redis for rate limit checks, so we guard against that here. + return if Feature.disabled?(:search_rate_limited_scopes) || abuse_detected? + + params[:scope] + end + + def abuse_detected? + Gitlab::Search::Params.new(params, detect_abuse: true).abusive? + end end diff --git a/app/controllers/concerns/send_file_upload.rb b/app/controllers/concerns/send_file_upload.rb index 2141b257b40..c8b987da58c 100644 --- a/app/controllers/concerns/send_file_upload.rb +++ b/app/controllers/concerns/send_file_upload.rb @@ -63,8 +63,7 @@ module SendFileUpload private def image_scaling_request?(file_upload) - (avatar_safe_for_scaling?(file_upload) || pwa_icon_safe_for_scaling?(file_upload)) && - scaling_allowed_by_feature_flags?(file_upload) + avatar_safe_for_scaling?(file_upload) || pwa_icon_safe_for_scaling?(file_upload) end def pwa_icon_safe_for_scaling?(file_upload) @@ -90,8 +89,4 @@ module SendFileUpload def valid_image_scaling_width?(allowed_scalar_widths) allowed_scalar_widths.include?(params[:width]&.to_i) end - - def scaling_allowed_by_feature_flags?(file_upload) - Feature.enabled?(:dynamic_image_resizing, type: :ops) - end end diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb index 1bb81a46e50..62c5aee16e4 100644 --- a/app/controllers/concerns/snippets_actions.rb +++ b/app/controllers/concerns/snippets_actions.rb @@ -9,13 +9,13 @@ module SnippetsActions include Gitlab::NoteableMetadata include Snippets::SendBlob include SnippetsSort - include RedisTracking + include ProductAnalyticsTracking included do skip_before_action :verify_authenticity_token, if: -> { action_name == 'show' && js_request? } - track_redis_hll_event :show, name: 'i_snippets_show' + track_event :show, name: 'i_snippets_show' respond_to :html end diff --git a/app/controllers/concerns/sorting_preference.rb b/app/controllers/concerns/sorting_preference.rb index 300c1d6d779..3dc1780d6fe 100644 --- a/app/controllers/concerns/sorting_preference.rb +++ b/app/controllers/concerns/sorting_preference.rb @@ -90,6 +90,10 @@ module SortingPreference return false unless sort_order return can_sort_by_issue_weight?(action_name == 'issues') if sort_order.include?('weight') + if sort_order.include?('merged_at') + return can_sort_by_merged_date?(controller_name == 'merge_requests' || action_name == 'merge_requests') + end + true end end diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb index 308da018a42..db756ae336f 100644 --- a/app/controllers/concerns/uploads_actions.rb +++ b/app/controllers/concerns/uploads_actions.rb @@ -5,11 +5,10 @@ module UploadsActions include Gitlab::Utils::StrongMemoize include SendFileUpload - UPLOAD_MOUNTS = %w[avatar attachment file logo pwa_icon header_logo favicon].freeze + UPLOAD_MOUNTS = %w[avatar attachment file logo pwa_icon header_logo favicon screenshot].freeze included do prepend_before_action :set_request_format_from_path_extension - skip_before_action :default_cache_headers, only: :show rescue_from FileUploader::InvalidSecret, with: :render_404 end diff --git a/app/controllers/concerns/verifies_with_email.rb b/app/controllers/concerns/verifies_with_email.rb index fb48c0d8ba5..45869c05f41 100644 --- a/app/controllers/concerns/verifies_with_email.rb +++ b/app/controllers/concerns/verifies_with_email.rb @@ -78,7 +78,7 @@ module VerifiesWithEmail def send_verification_instructions(user) return if send_rate_limited?(user) - service = Users::EmailVerification::GenerateTokenService.new(attr: :unlock_token) + service = Users::EmailVerification::GenerateTokenService.new(attr: :unlock_token, user: user) raw_token, encrypted_token = service.execute user.unlock_token = encrypted_token user.lock_access!({ send_instructions: false }) diff --git a/app/controllers/concerns/vscode_cdn_csp.rb b/app/controllers/concerns/vscode_cdn_csp.rb deleted file mode 100644 index dc8cea966e5..00000000000 --- a/app/controllers/concerns/vscode_cdn_csp.rb +++ /dev/null @@ -1,17 +0,0 @@ -# rubocop:disable Naming/FileName -# frozen_string_literal: true - -module VSCodeCDNCSP - extend ActiveSupport::Concern - - included do - content_security_policy do |policy| - next if policy.directives.blank? - - default_src = Array(policy.directives['default-src'] || []) - policy.directives['frame-src'] ||= default_src - policy.directives['frame-src'].concat(['https://*.vscode-cdn.net/']) - end - end -end -# rubocop:enable Naming/FileName diff --git a/app/controllers/concerns/web_hooks/hook_actions.rb b/app/controllers/concerns/web_hooks/hook_actions.rb index f61600af951..ae971b7bc95 100644 --- a/app/controllers/concerns/web_hooks/hook_actions.rb +++ b/app/controllers/concerns/web_hooks/hook_actions.rb @@ -7,6 +7,8 @@ module WebHooks included do attr_writer :hooks, :hook + + before_action :hook_logs, only: :edit end def index @@ -80,5 +82,9 @@ module WebHooks flash[:alert] = result[:message] end end + + def hook_logs + @hook_logs ||= hook.web_hook_logs.recent.page(params[:page]).without_count + end end end diff --git a/app/controllers/concerns/web_ide_csp.rb b/app/controllers/concerns/web_ide_csp.rb new file mode 100644 index 00000000000..c2d66abb538 --- /dev/null +++ b/app/controllers/concerns/web_ide_csp.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module WebIdeCSP + extend ActiveSupport::Concern + + included do + before_action :include_web_ide_csp + + # We want to include frames from `/assets/webpack` of the request's host to + # support URL flexibility with the Web IDE. + # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/118875 + def include_web_ide_csp + return if request.content_security_policy.directives.blank? + + base_uri = URI(request.url) + base_uri.path = ::Gitlab.config.gitlab.relative_url_root || '/' + # `.path +=` handles combining `x/` and `/foo` + base_uri.path += '/assets/webpack/' + webpack_url = base_uri.to_s + + default_src = Array(request.content_security_policy.directives['default-src'] || []) + request.content_security_policy.directives['frame-src'] ||= default_src + request.content_security_policy.directives['frame-src'].concat([webpack_url, 'https://*.vscode-cdn.net/']) + + request.content_security_policy.directives['worker-src'] ||= default_src + request.content_security_policy.directives['worker-src'].concat([webpack_url]) + end + end +end diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb index 2b781c528ad..265cf2a7698 100644 --- a/app/controllers/concerns/wiki_actions.rb +++ b/app/controllers/concerns/wiki_actions.rb @@ -5,12 +5,21 @@ module WikiActions include PreviewMarkdown include SendsBlob include Gitlab::Utils::StrongMemoize - include RedisTracking + include ProductAnalyticsTracking extend ActiveSupport::Concern RESCUE_GIT_TIMEOUTS_IN = %w[show edit history diff pages].freeze included do + content_security_policy do |p| + next if p.directives.blank? + + default_frame_src = p.directives['frame-src'] || p.directives['default-src'] + frame_src_values = Array.wrap(default_frame_src) | ['https://embed.diagrams.net'].compact + + p.frame_src(*frame_src_values) + end + before_action { respond_to :html } before_action :authorize_read_wiki! @@ -37,9 +46,7 @@ module WikiActions end end - # NOTE: We want to include wiki page views in the same counter as the other - # Event-based wiki actions tracked through TrackUniqueEvents, so we use the same event name. - track_redis_hll_event :show, name: Gitlab::UsageDataCounters::TrackUniqueEvents::WIKI_ACTION.to_s + track_event :show, name: 'wiki_action' helper_method :view_file_button, :diff_file_html_data @@ -48,6 +55,12 @@ module WikiActions render 'shared/wikis/git_error' end + + rescue_from Gitlab::Git::Repository::NoRepository do + @error = _('Could not access the Wiki Repository at this time.') + + render 'shared/wikis/empty' + end end def new @@ -106,7 +119,11 @@ module WikiActions def update return render('shared/wikis/empty') unless can?(current_user, :create_wiki, container) - response = WikiPages::UpdateService.new(container: container, current_user: current_user, params: wiki_params).execute(page) + response = WikiPages::UpdateService.new( + container: container, + current_user: current_user, + params: wiki_params + ).execute(page) @page = response.payload[:page] if response.success? @@ -142,8 +159,7 @@ module WikiActions # rubocop:disable Gitlab/ModuleWithInstanceVariables def history if page - @commits = Kaminari.paginate_array(page.versions(page: params[:page].to_i), - total_count: page.count_versions) + @commits = Kaminari.paginate_array(page.versions(page: params[:page].to_i), total_count: page.count_versions) .page(params[:page]) render 'shared/wikis/history' @@ -178,8 +194,7 @@ module WikiActions if response.success? flash[:toast] = _("Wiki page was successfully deleted.") - redirect_to wiki_path(wiki), - status: :found + redirect_to wiki_path(wiki), status: :found else @error = response.message render 'shared/wikis/edit' diff --git a/app/controllers/confirmations_controller.rb b/app/controllers/confirmations_controller.rb index 6dd4d72bbc7..e94138c4d9b 100644 --- a/app/controllers/confirmations_controller.rb +++ b/app/controllers/confirmations_controller.rb @@ -10,7 +10,7 @@ class ConfirmationsController < Devise::ConfirmationsController prepend_before_action :check_recaptcha, only: :create before_action :load_recaptcha, only: :new - feature_category :authentication_and_authorization + feature_category :user_management def almost_there flash[:notice] = nil @@ -20,12 +20,12 @@ class ConfirmationsController < Devise::ConfirmationsController protected def after_resending_confirmation_instructions_path_for(resource) - return users_almost_there_path unless Feature.enabled?(:soft_email_confirmation) + return users_almost_there_path unless Gitlab::CurrentSettings.email_confirmation_setting_soft? stored_location_for(resource) || dashboard_projects_path end - def after_confirmation_path_for(resource_name, resource) + def after_confirmation_path_for(_resource_name, resource) accept_pending_invitations # incoming resource can either be a :user or an :email @@ -34,10 +34,14 @@ class ConfirmationsController < Devise::ConfirmationsController else Gitlab::AppLogger.info("Email Confirmed: username=#{resource.username} email=#{resource.email} ip=#{request.remote_ip}") flash[:notice] = flash[:notice] + _(" Please sign in.") - new_session_path(:user, anchor: 'login-pane', invite_email: resource.email) + sign_in_path(resource) end end + def sign_in_path(user) + new_session_path(:user, anchor: 'login-pane', invite_email: resource.email) + end + def check_recaptcha return unless resource_params[:email].present? diff --git a/app/controllers/dashboard/application_controller.rb b/app/controllers/dashboard/application_controller.rb index 95deacdc5b9..80c65948fff 100644 --- a/app/controllers/dashboard/application_controller.rb +++ b/app/controllers/dashboard/application_controller.rb @@ -14,3 +14,5 @@ class Dashboard::ApplicationController < ApplicationController @projects ||= current_user.authorized_projects.sorted_by_updated_desc.non_archived end end + +Dashboard::ApplicationController.prepend_mod diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index 89d362c88a4..e26ac083622 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -66,8 +66,8 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController end def load_projects(finder_params) - @total_user_projects_count = ProjectsFinder.new(params: { non_public: true, not_aimed_for_deletion: true }, current_user: current_user).execute - @total_starred_projects_count = ProjectsFinder.new(params: { starred: true, not_aimed_for_deletion: true }, current_user: current_user).execute + @all_user_projects = ProjectsFinder.new(params: { non_public: true, archived: false, not_aimed_for_deletion: true }, current_user: current_user).execute + @all_starred_projects = ProjectsFinder.new(params: { starred: true, archived: false, not_aimed_for_deletion: true }, current_user: current_user).execute finder_params[:use_cte] = true if use_cte_for_finder? diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb index 3005d19f8ed..a1b8dbcd304 100644 --- a/app/controllers/dashboard/todos_controller.rb +++ b/app/controllers/dashboard/todos_controller.rb @@ -29,9 +29,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController respond_to do |format| format.html do - redirect_to dashboard_todos_path, - status: :found, - notice: _('To-do item successfully marked as done.') + redirect_to dashboard_todos_path, status: :found, notice: _('To-do item successfully marked as done.') end format.js { head :ok } format.json { render json: todos_counts } diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index b003ca564f3..d70b2e57a95 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -12,6 +12,10 @@ class DashboardController < Dashboard::ApplicationController before_action :set_show_full_reference, only: [:issues, :merge_requests] before_action :check_filters_presence!, only: [:issues, :merge_requests] + before_action only: :merge_requests do + push_frontend_feature_flag(:mr_approved_filter, type: :ops) + end + respond_to :html feature_category :user_profile, [:activity] diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb index ac355b861b3..96a7b5b144d 100644 --- a/app/controllers/explore/groups_controller.rb +++ b/app/controllers/explore/groups_controller.rb @@ -7,7 +7,12 @@ class Explore::GroupsController < Explore::ApplicationController urgency :low def index - user = Feature.enabled?(:generic_explore_groups, current_user, type: :experiment) ? nil : current_user + # For gitlab.com, including internal visibility groups here causes + # a major performance issue: https://gitlab.com/gitlab-org/gitlab/-/issues/358944 + # + # For self-hosted users, not including internal groups here causes + # a lack of visibility: https://gitlab.com/gitlab-org/gitlab/-/issues/389041 + user = Gitlab.com? ? nil : current_user render_group_tree GroupsFinder.new(user).execute end diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index 34745815f3d..eebcbe88ebf 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -88,8 +88,8 @@ class Explore::ProjectsController < Explore::ApplicationController private def load_project_counts - @total_user_projects_count = ProjectsFinder.new(params: { non_public: true }, current_user: current_user).execute - @total_starred_projects_count = ProjectsFinder.new(params: { starred: true }, current_user: current_user).execute + @all_user_projects = ProjectsFinder.new(params: { non_public: true }, current_user: current_user).execute + @all_starred_projects = ProjectsFinder.new(params: { starred: true }, current_user: current_user).execute end def load_projects diff --git a/app/controllers/google_api/authorizations_controller.rb b/app/controllers/google_api/authorizations_controller.rb index 536c5e347e7..8a3183ba615 100644 --- a/app/controllers/google_api/authorizations_controller.rb +++ b/app/controllers/google_api/authorizations_controller.rb @@ -6,7 +6,7 @@ module GoogleApi before_action :validate_session_key! - feature_category :kubernetes_management + feature_category :deployment_management urgency :low ## diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index 2f01bdecd23..ff4fce9ad1e 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -76,6 +76,13 @@ class GraphqlController < ApplicationController render_error(exception.message, status: :forbidden) end + rescue_from Gitlab::Git::ResourceExhaustedError do |exception| + log_exception(exception) + + response.headers.merge!(exception.headers) + render_error(exception.message, status: :too_many_requests) + end + rescue_from Gitlab::Graphql::Variables::Invalid do |exception| render_error(exception.message, status: :unprocessable_entity) end @@ -102,6 +109,10 @@ class GraphqlController < ApplicationController private + def permitted_multiplex_params + params.permit(_json: [:query, :operationName, { variables: {} }]) + end + def disallow_mutations_for_get return unless request.get? || request.head? return unless any_mutating_query? @@ -111,7 +122,7 @@ class GraphqlController < ApplicationController def limit_query_size total_size = if multiplex? - params[:_json].sum { _1[:query].size } + multiplex_param.sum { _1[:query].size } else query.size end @@ -178,8 +189,12 @@ class GraphqlController < ApplicationController params.fetch(:query, '') end + def multiplex_param + permitted_multiplex_params[:_json] + end + def multiplex_queries - params[:_json].map do |single_query_info| + multiplex_param.map do |single_query_info| { query: single_query_info[:query], variables: build_variables(single_query_info[:variables]), @@ -206,8 +221,10 @@ class GraphqlController < ApplicationController Gitlab::Graphql::Variables.new(variable_info).to_h end + # We support Apollo-style query batching where an array of queries will be in the `_json:` key. + # https://graphql-ruby.org/queries/multiplex.html#apollo-query-batching def multiplex? - params[:_json].present? + params[:_json].is_a?(Array) end def authorize_access_api! diff --git a/app/controllers/groups/achievements_controller.rb b/app/controllers/groups/achievements_controller.rb new file mode 100644 index 00000000000..52d63761819 --- /dev/null +++ b/app/controllers/groups/achievements_controller.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Groups + class AchievementsController < Groups::ApplicationController + feature_category :user_profile + urgency :low + + before_action :authorize_read_achievement! + + private + + def authorize_read_achievement! + render_404 unless can?(current_user, :read_achievement, group) + end + end +end diff --git a/app/controllers/groups/children_controller.rb b/app/controllers/groups/children_controller.rb index d10c52f0301..ca3be1542aa 100644 --- a/app/controllers/groups/children_controller.rb +++ b/app/controllers/groups/children_controller.rb @@ -5,6 +5,8 @@ module Groups extend ::Gitlab::Utils::Override before_action :group + before_action :validate_per_page + skip_cross_project_access_check :index feature_category :subgroups @@ -41,10 +43,11 @@ module Groups protected def setup_children(parent) - @children = GroupDescendantsFinder.new(current_user: current_user, - parent_group: parent, - params: params.to_unsafe_h).execute - @children = @children.page(params[:page]) + @children = GroupDescendantsFinder.new( + current_user: current_user, + parent_group: parent, + params: group_descendants_params + ).execute.page(params[:page]) end private @@ -53,5 +56,25 @@ module Groups def has_project_list? true end + + def group_descendants_params + @group_descendants_params ||= params.to_unsafe_h.compact + end + + def validate_per_page + return unless group_descendants_params.key?(:per_page) + + per_page = begin + Integer(group_descendants_params[:per_page]) + rescue ArgumentError, TypeError + 0 + end + + respond_to do |format| + format.json do + render status: :bad_request, json: { message: 'per_page does not have a valid value' } if per_page < 1 + end + end + end end end diff --git a/app/controllers/groups/dependency_proxy_for_containers_controller.rb b/app/controllers/groups/dependency_proxy_for_containers_controller.rb index 427df9a7129..1b1aed0ec2e 100644 --- a/app/controllers/groups/dependency_proxy_for_containers_controller.rb +++ b/app/controllers/groups/dependency_proxy_for_containers_controller.rb @@ -172,6 +172,6 @@ class Groups::DependencyProxyForContainersController < ::Groups::DependencyProxy end def manifest_header - token_header.merge(Accept: ::ContainerRegistry::Client::ACCEPTED_TYPES) + token_header.merge(Accept: ::DependencyProxy::Manifest::ACCEPTED_TYPES) end end diff --git a/app/controllers/groups/group_links_controller.rb b/app/controllers/groups/group_links_controller.rb index cc2ca728592..c74c48a960d 100644 --- a/app/controllers/groups/group_links_controller.rb +++ b/app/controllers/groups/group_links_controller.rb @@ -7,7 +7,7 @@ class Groups::GroupLinksController < Groups::ApplicationController feature_category :subgroups def update - Groups::GroupLinks::UpdateService.new(@group_link).execute(group_link_params) + Groups::GroupLinks::UpdateService.new(@group_link, current_user).execute(group_link_params) if @group_link.expires? render json: { diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index f0b857ca4c9..d614cc1cb24 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -16,10 +16,13 @@ class Groups::GroupMembersController < Groups::ApplicationController before_action :authorize_admin_group_member!, except: admin_not_required_endpoints before_action :authorize_read_group_member!, only: :index + before_action only: [:index] do + push_frontend_feature_flag(:service_accounts_crud, @group) + end + skip_before_action :check_two_factor_requirement, only: :leave skip_cross_project_access_check :index, :update, :destroy, :request_access, - :approve_access_request, :leave, :resend_invite, - :override + :approve_access_request, :leave, :resend_invite, :override feature_category :subgroups urgency :low @@ -73,7 +76,7 @@ class Groups::GroupMembersController < Groups::ApplicationController end def filter_params - params.permit(:two_factor, :search).merge(sort: @sort) + params.permit(:two_factor, :search, :user_type).merge(sort: @sort) end def membershipable_members diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 494b8c5621d..903c8c214ae 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -45,6 +45,24 @@ class Groups::MilestonesController < Groups::ApplicationController Milestones::UpdateService.new(@milestone.parent, current_user, milestone_params).execute(@milestone) redirect_to milestone_path(@milestone) + rescue ActiveRecord::StaleObjectError + respond_to do |format| + format.html do + @conflict = true + render :edit + end + + format.json do + render json: { + errors: [ + format( + _("Someone edited this %{model_name} at the same time you did. Please refresh your browser and make sure your changes will not unintentionally remove theirs."), # rubocop:disable Layout/LineLength + model_name: _('milestone') + ) + ] + }, status: :conflict + end + end end def destroy @@ -63,7 +81,15 @@ class Groups::MilestonesController < Groups::ApplicationController end def milestone_params - params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event) + params.require(:milestone) + .permit( + :description, + :due_date, + :lock_version, + :start_date, + :state_event, + :title + ) end def milestones diff --git a/app/controllers/groups/observability_controller.rb b/app/controllers/groups/observability_controller.rb index 726af00a10e..525407f5849 100644 --- a/app/controllers/groups/observability_controller.rb +++ b/app/controllers/groups/observability_controller.rb @@ -30,7 +30,7 @@ module Groups end def check_observability_allowed - render_404 unless Gitlab::Observability.observability_enabled?(current_user, group) + render_404 unless Gitlab::Observability.allowed_for_action?(current_user, group, params[:action]) end end end diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb index 859bb0adb4e..4b52617d287 100644 --- a/app/controllers/groups/runners_controller.rb +++ b/app/controllers/groups/runners_controller.rb @@ -2,14 +2,20 @@ class Groups::RunnersController < Groups::ApplicationController before_action :authorize_read_group_runners!, only: [:index, :show] + before_action :authorize_create_group_runners!, only: [:new, :register] before_action :authorize_update_runner!, only: [:edit, :update, :destroy, :pause, :resume] - before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show] + before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show, :register] + + before_action only: [:index] do + push_frontend_feature_flag(:create_runner_workflow_for_namespace, group) + end feature_category :runner urgency :low def index @group_runner_registration_token = @group.runners_token if can?(current_user, :register_group_runners, group) + @group_new_runner_path = new_group_runner_path(@group) if can?(current_user, :create_runner, group) Gitlab::Tracking.event(self.class.name, 'index', user: current_user, namespace: @group) end @@ -28,6 +34,14 @@ class Groups::RunnersController < Groups::ApplicationController end end + def new + render_404 unless create_runner_workflow_for_namespace_enabled? + end + + def register + render_404 unless create_runner_workflow_for_namespace_enabled? && runner.registration_available? + end + private def runner @@ -47,6 +61,16 @@ class Groups::RunnersController < Groups::ApplicationController render_404 end + + def authorize_create_group_runners! + return if can?(current_user, :create_runner, group) + + render_404 + end + + def create_runner_workflow_for_namespace_enabled? + Feature.enabled?(:create_runner_workflow_for_namespace, group) + end end Groups::RunnersController.prepend_mod diff --git a/app/controllers/groups/settings/access_tokens_controller.rb b/app/controllers/groups/settings/access_tokens_controller.rb index d86ddcfe2d0..ff07e881bfa 100644 --- a/app/controllers/groups/settings/access_tokens_controller.rb +++ b/app/controllers/groups/settings/access_tokens_controller.rb @@ -7,7 +7,7 @@ module Groups include AccessTokensActions layout 'group_settings' - feature_category :authentication_and_authorization + feature_category :system_access alias_method :resource, :group diff --git a/app/controllers/groups/settings/applications_controller.rb b/app/controllers/groups/settings/applications_controller.rb index 3557d485422..3ae1ae824a0 100644 --- a/app/controllers/groups/settings/applications_controller.rb +++ b/app/controllers/groups/settings/applications_controller.rb @@ -6,18 +6,16 @@ module Groups include OauthApplications prepend_before_action :authorize_admin_group! - before_action :set_application, only: [:show, :edit, :update, :destroy] + before_action :set_application, only: [:show, :edit, :update, :renew, :destroy] before_action :load_scopes, only: [:index, :create, :edit, :update] - feature_category :authentication_and_authorization + feature_category :system_access def index set_index_vars end - def show - @created = get_created_session if Feature.disabled?('hash_oauth_secrets') - end + def show; end def edit end @@ -28,15 +26,8 @@ module Groups if @application.persisted? flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) - if Feature.enabled?('hash_oauth_secrets') - - @created = true - render :show - else - set_created_session - - redirect_to group_settings_application_url(@group, @application) - end + @created = true + render :show else set_index_vars render :index @@ -51,6 +42,16 @@ module Groups end end + def renew + @application.renew_secret + + if @application.save + render json: { secret: @application.plaintext_secret } + else + render json: { errors: @application.errors }, status: :unprocessable_entity + end + end + def destroy @application.destroy redirect_to group_settings_applications_url(@group), status: :found, notice: _('Application was successfully destroyed.') diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb index 78e3ffa4af9..4bbaf92b126 100644 --- a/app/controllers/groups/settings/ci_cd_controller.rb +++ b/app/controllers/groups/settings/ci_cd_controller.rb @@ -12,6 +12,11 @@ module Groups before_action :assign_variables_to_gon, only: [:show] feature_category :continuous_integration + + before_action do + push_frontend_feature_flag(:ci_variables_pages, current_user) + end + urgency :low def show diff --git a/app/controllers/groups/usage_quotas_controller.rb b/app/controllers/groups/usage_quotas_controller.rb index 4f858cd130a..125c8fde004 100644 --- a/app/controllers/groups/usage_quotas_controller.rb +++ b/app/controllers/groups/usage_quotas_controller.rb @@ -6,7 +6,7 @@ module Groups before_action :verify_usage_quotas_enabled! before_action :push_frontend_feature_flags - feature_category :subscription_cost_management + feature_category :consumables_cost_management urgency :low def index diff --git a/app/controllers/groups/variables_controller.rb b/app/controllers/groups/variables_controller.rb index 9ddf6c80c70..fad3a6ab9f5 100644 --- a/app/controllers/groups/variables_controller.rb +++ b/app/controllers/groups/variables_controller.rb @@ -6,7 +6,7 @@ module Groups skip_cross_project_access_check :show, :update - feature_category :pipeline_authoring + feature_category :secrets_management urgency :low, [:show] diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 8f7a2c177b7..d2f65104d86 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -32,22 +32,19 @@ class GroupsController < Groups::ApplicationController before_action :check_export_rate_limit!, only: [:export, :download_export] - before_action :track_experiment_event, only: [:new] - before_action only: :issues do push_frontend_feature_flag(:or_issuable_queries, group) push_frontend_feature_flag(:frontend_caching, group) push_force_frontend_feature_flag(:work_items, group.work_items_feature_flag_enabled?) end - before_action only: :show do - push_frontend_feature_flag(:show_group_readme, group) + before_action only: :merge_requests do + push_frontend_feature_flag(:mr_approved_filter, type: :ops) end helper_method :captcha_required? - skip_cross_project_access_check :index, :new, :create, :edit, :update, - :destroy, :projects + skip_cross_project_access_check :index, :new, :create, :edit, :update, :destroy, :projects # When loading show as an atom feed, we render events that could leak cross # project information skip_cross_project_access_check :show, if: -> { request.format.html? } @@ -76,6 +73,7 @@ class GroupsController < Groups::ApplicationController end def new + @parent_group = Group.find_by_id(params[:parent_id]) @group = Group.new(params.permit(:parent_id)) @group.build_namespace_settings end @@ -201,7 +199,7 @@ class GroupsController < Groups::ApplicationController send_upload(@group.export_file, attachment: @group.export_file.filename) else redirect_to edit_group_path(@group), - alert: _('The file containing the export is not available yet; it may still be transferring. Please try again later.') + alert: _('The file containing the export is not available yet; it may still be transferring. Please try again later.') end else redirect_to edit_group_path(@group), @@ -402,12 +400,6 @@ class GroupsController < Groups::ApplicationController captcha_enabled? && !params[:parent_id] end - def track_experiment_event - return if params[:parent_id] - - experiment(:require_verification_for_namespace_creation, user: current_user).track(:start_create_group) - end - def group_feature_attributes [] end diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb index d0e14000d8e..4cc943ac252 100644 --- a/app/controllers/ide_controller.rb +++ b/app/controllers/ide_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class IdeController < ApplicationController - include VSCodeCDNCSP + include WebIdeCSP include StaticObjectExternalStorageCSP include Gitlab::Utils::StrongMemoize @@ -10,7 +10,6 @@ class IdeController < ApplicationController before_action do push_frontend_feature_flag(:build_service_proxy) push_frontend_feature_flag(:reject_unsigned_commits_by_gitlab) - define_index_vars end feature_category :web_ide @@ -20,9 +19,9 @@ class IdeController < ApplicationController def index Gitlab::UsageDataCounters::WebIdeCounter.increment_views_count - if project && Feature.enabled?(:route_hll_to_snowplow_phase2, project&.namespace) - Gitlab::Tracking.event(self.class.to_s, 'web_ide_views', - namespace: project&.namespace, user: current_user) + if project + Gitlab::Tracking.event(self.class.to_s, 'web_ide_views', namespace: project.namespace, user: current_user) + @fork_info = fork_info(project, params[:branch]) end render layout: 'fullscreen', locals: { minimal: helpers.use_new_web_ide? } @@ -34,15 +33,6 @@ class IdeController < ApplicationController render_404 unless can?(current_user, :read_project, project) end - def define_index_vars - return unless project - - @branch = params[:branch] - @path = params[:path] - @merge_request = params[:merge_request_id] - @fork_info = fork_info(project, @branch) - end - def fork_info(project, branch) return if can?(current_user, :push_code, project) diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb index 7ef07032913..bcb6aed9e38 100644 --- a/app/controllers/import/base_controller.rb +++ b/app/controllers/import/base_controller.rb @@ -18,7 +18,7 @@ class Import::BaseController < ApplicationController if params[:namespace_id]&.present? @namespace = Namespace.find_by_id(params[:namespace_id]) - render_404 unless current_user.can?(:create_projects, @namespace) + render_404 unless current_user.can?(:import_projects, @namespace) end end end diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb index 8a0f4a36781..c933b05e0c4 100644 --- a/app/controllers/import/bitbucket_controller.rb +++ b/app/controllers/import/bitbucket_controller.rb @@ -57,7 +57,7 @@ class Import::BitbucketController < Import::BaseController extra: { user_role: user_role(current_user, target_namespace), import_type: 'bitbucket' } ) - if current_user.can?(:create_projects, target_namespace) + if current_user.can?(:import_projects, target_namespace) # The token in a session can be expired, we need to get most recent one because # Bitbucket::Connection class refreshes it. session[:bitbucket_token] = bitbucket_client.connection.token @@ -70,7 +70,7 @@ class Import::BitbucketController < Import::BaseController render json: { errors: project_save_error(project) }, status: :unprocessable_entity end else - render json: { errors: _('This namespace has already been taken! Please choose another one.') }, status: :unprocessable_entity + render json: { errors: _('You are not allowed to import projects in this namespace.') }, status: :unprocessable_entity end end diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb index f4eea3abd32..d7d7ad84bc8 100644 --- a/app/controllers/import/bulk_imports_controller.rb +++ b/app/controllers/import/bulk_imports_controller.rb @@ -5,9 +5,6 @@ class Import::BulkImportsController < ApplicationController before_action :ensure_bulk_import_enabled before_action :verify_blocked_uri, only: :status - before_action only: :status do - push_frontend_feature_flag(:bulk_import_projects) - end feature_category :importers urgency :low diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb index 77043e174b4..9ee8e59053f 100644 --- a/app/controllers/import/fogbugz_controller.rb +++ b/app/controllers/import/fogbugz_controller.rb @@ -19,7 +19,7 @@ class Import::FogbugzController < Import::BaseController # If the URI is invalid various errors can occur return redirect_to new_import_fogbugz_path(namespace_id: params[:namespace_id]), alert: _('Could not connect to FogBugz, check your URL') end - session[:fogbugz_token] = res.get_token + session[:fogbugz_token] = res.get_token.to_s session[:fogbugz_uri] = params[:uri] redirect_to new_user_map_import_fogbugz_path(namespace_id: params[:namespace_id]) diff --git a/app/controllers/import/gitea_controller.rb b/app/controllers/import/gitea_controller.rb index 61e32650db3..2778b97419a 100644 --- a/app/controllers/import/gitea_controller.rb +++ b/app/controllers/import/gitea_controller.rb @@ -32,7 +32,7 @@ class Import::GiteaController < Import::GithubController if params[:namespace_id].present? @namespace = Namespace.find_by_id(params[:namespace_id]) - render_404 unless current_user.can?(:create_projects, @namespace) + render_404 unless current_user.can?(:import_projects, @namespace) end end end @@ -71,6 +71,11 @@ class Import::GiteaController < Import::GithubController end end + override :serialized_imported_projects + def serialized_imported_projects(projects = already_added_projects) + ProjectSerializer.new.represent(projects, serializer: :import, provider_url: provider_url) + end + override :client_repos def client_repos @client_repos ||= filtered(client.repos) diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index 0bee1faccf5..41477519ba5 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -11,6 +11,10 @@ class Import::GithubController < Import::BaseController before_action :provider_auth, only: [:status, :realtime_changes, :create] before_action :expire_etag_cache, only: [:status, :create] + before_action only: [:status] do + push_frontend_feature_flag(:import_details_page) + end + rescue_from Octokit::Unauthorized, with: :provider_unauthorized rescue_from Octokit::TooManyRequests, with: :provider_rate_limit rescue_from Gitlab::GithubImport::RateLimitError, with: :rate_limit_threshold_exceeded @@ -53,19 +57,24 @@ class Import::GithubController < Import::BaseController render json: { imported_projects: serialized_imported_projects, provider_repos: serialized_provider_repos, incompatible_repos: serialized_incompatible_repos, - page_info: client_repos_response[:page_info] } + page_info: client_repos_response[:page_info], + provider_repo_count: client_repos_response[:count] } end format.html do if params[:namespace_id].present? @namespace = Namespace.find_by_id(params[:namespace_id]) - render_404 unless current_user.can?(:create_projects, @namespace) + render_404 unless current_user.can?(:import_projects, @namespace) end end end end + def details + render_404 unless Feature.enabled?(:import_details_page) + end + def create result = Import::GithubService.new(client, current_user, import_params).execute(access_params, provider_name) @@ -82,6 +91,21 @@ class Import::GithubController < Import::BaseController render json: Import::GithubRealtimeRepoSerializer.new.represent(already_added_projects) end + def failures + project = Project.imported_from(provider_name).find(params[:project_id]) + + unless project.import_finished? + return render status: :bad_request, json: { + message: _('The import is not complete.') + } + end + + failures = project.import_failures.with_external_identifiers + serializer = Import::GithubFailureSerializer.new.with_pagination(request, response) + + render json: serializer.represent(failures) + end + def cancel project = Project.imported_from(provider_name).find(params[:project_id]) result = Import::Github::CancelProjectImportService.new(project, current_user).execute @@ -110,6 +134,14 @@ class Import::GithubController < Import::BaseController render json: canceled end + def counts + render json: { + owned: client_proxy.count_repos_by('owned', current_user.id), + collaborated: client_proxy.count_repos_by('collaborated', current_user.id), + organization: client_proxy.count_repos_by('organization', current_user.id) + } + end + protected override :importable_repos @@ -145,7 +177,10 @@ class Import::GithubController < Import::BaseController end def serialized_imported_projects(projects = already_added_projects) - ProjectSerializer.new.represent(projects, serializer: :import, provider_url: provider_url) + ProjectSerializer.new.represent( + projects, + serializer: :import, provider_url: provider_url, client: client_proxy + ) end def expire_etag_cache @@ -245,11 +280,7 @@ class Import::GithubController < Import::BaseController { before: params[:before].presence, after: params[:after].presence, - first: PAGE_LENGTH, - # TODO: remove after rollout FF github_client_fetch_repos_via_graphql - # https://gitlab.com/gitlab-org/gitlab/-/issues/385649 - page: [1, params[:page].to_i].max, - per_page: PAGE_LENGTH + first: PAGE_LENGTH } end diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb deleted file mode 100644 index dd25698d0a9..00000000000 --- a/app/controllers/import/gitlab_controller.rb +++ /dev/null @@ -1,92 +0,0 @@ -# frozen_string_literal: true - -class Import::GitlabController < Import::BaseController - extend ::Gitlab::Utils::Override - - MAX_PROJECT_PAGES = 15 - PER_PAGE_PROJECTS = 100 - - before_action :verify_gitlab_import_enabled - before_action :gitlab_auth, except: :callback - - rescue_from OAuth2::Error, with: :gitlab_unauthorized - - def callback - session[:gitlab_access_token] = client.get_token(params[:code], callback_import_gitlab_url(namespace_id: params[:namespace_id])) - redirect_to status_import_gitlab_url(namespace_id: params[:namespace_id]) - end - - # We need to re-expose controller's internal method 'status' as action. - # rubocop:disable Lint/UselessMethodDefinition - def status - super - end - # rubocop:enable Lint/UselessMethodDefinition - - def create - repo = client.project(params[:repo_id].to_i) - target_namespace = find_or_create_namespace(repo['namespace']['path'], client.user['username']) - - if current_user.can?(:create_projects, target_namespace) - project = Gitlab::GitlabImport::ProjectCreator.new(repo, target_namespace, current_user, access_params).execute - - if project.persisted? - render json: ProjectSerializer.new.represent(project, serializer: :import) - else - render json: { errors: project_save_error(project) }, status: :unprocessable_entity - end - else - render json: { errors: _('This namespace has already been taken! Please choose another one.') }, status: :unprocessable_entity - end - end - - protected - - override :importable_repos - def importable_repos - client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS).to_a - end - - override :incompatible_repos - def incompatible_repos - [] - end - - override :provider_name - def provider_name - :gitlab - end - - override :provider_url - def provider_url - 'https://gitlab.com' - end - - private - - def client - @client ||= Gitlab::GitlabImport::Client.new(session[:gitlab_access_token]) - end - - def verify_gitlab_import_enabled - render_404 unless gitlab_import_enabled? - end - - def gitlab_auth - if session[:gitlab_access_token].blank? - go_to_gitlab_for_permissions - end - end - - def go_to_gitlab_for_permissions - redirect_to client.authorize_url(callback_import_gitlab_url(namespace_id: params[:namespace_id])) - end - - def gitlab_unauthorized - go_to_gitlab_for_permissions - end - - def access_params - { gitlab_access_token: session[:gitlab_access_token] } - end -end diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb index 9b8c480e529..d1b182a57d8 100644 --- a/app/controllers/import/gitlab_projects_controller.rb +++ b/app/controllers/import/gitlab_projects_controller.rb @@ -8,7 +8,7 @@ class Import::GitlabProjectsController < Import::BaseController def new @namespace = Namespace.find(project_params[:namespace_id]) - return render_404 unless current_user.can?(:create_projects, @namespace) + return render_404 unless current_user.can?(:import_projects, @namespace) @path = project_params[:path] end diff --git a/app/controllers/import/manifest_controller.rb b/app/controllers/import/manifest_controller.rb index 461ba982969..03884717e54 100644 --- a/app/controllers/import/manifest_controller.rb +++ b/app/controllers/import/manifest_controller.rb @@ -20,8 +20,8 @@ class Import::ManifestController < Import::BaseController def upload group = Group.find(params[:group_id]) - unless can?(current_user, :create_projects, group) - @errors = ["You don't have enough permissions to create projects in the selected group"] + unless can?(current_user, :import_projects, group) + @errors = ["You don't have enough permissions to import projects in the selected group"] render :new && return end diff --git a/app/controllers/import/phabricator_controller.rb b/app/controllers/import/phabricator_controller.rb deleted file mode 100644 index d1c04817689..00000000000 --- a/app/controllers/import/phabricator_controller.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -class Import::PhabricatorController < Import::BaseController - include ImportHelper - - before_action :verify_import_enabled - - def new - end - - def create - @project = Gitlab::PhabricatorImport::ProjectCreator - .new(current_user, import_params).execute - - if @project&.persisted? - redirect_to @project - else - @name = params[:name] - @path = params[:path] - @errors = @project&.errors&.full_messages || [_("Invalid import params")] - - render :new - end - end - - def verify_import_enabled - render_404 unless phabricator_import_enabled? - end - - private - - def import_params - params.permit(:path, :phabricator_server_url, :api_token, :name, :namespace_id) - end -end diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index 2a7f2d42e2a..8a8ae38c6f3 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -13,7 +13,7 @@ class InvitesController < ApplicationController respond_to :html - feature_category :authentication_and_authorization + feature_category :system_access def show accept if skip_invitation_prompt? @@ -83,7 +83,7 @@ class InvitesController < ApplicationController def authenticate_user! return if current_user - store_location_for(:user, invite_landing_url) if member + store_location_for(:user, invite_details[:path]) if member if user_sign_up? set_session_invite_params @@ -120,10 +120,6 @@ class InvitesController < ApplicationController end end - def invite_landing_url - root_url + invite_details[:path] - end - def invite_details @invite_details ||= case member.source when Project diff --git a/app/controllers/jira_connect/branches_controller.rb b/app/controllers/jira_connect/branches_controller.rb index 12ea6560e3a..4c1b0d2b208 100644 --- a/app/controllers/jira_connect/branches_controller.rb +++ b/app/controllers/jira_connect/branches_controller.rb @@ -23,7 +23,8 @@ class JiraConnect::BranchesController < ApplicationController def new_branch_data { initial_branch_name: initial_branch_name, - success_state_svg_path: ActionController::Base.helpers.image_path('illustrations/merge_requests.svg') + success_state_svg_path: + ActionController::Base.helpers.image_path('illustrations/empty-state/empty-merge-requests-md.svg') } end end diff --git a/app/controllers/jira_connect/public_keys_controller.rb b/app/controllers/jira_connect/public_keys_controller.rb index 4505ab16926..8cb932c087f 100644 --- a/app/controllers/jira_connect/public_keys_controller.rb +++ b/app/controllers/jira_connect/public_keys_controller.rb @@ -22,8 +22,6 @@ module JiraConnect end def public_key_storage_enabled? - return true if Gitlab.config.jira_connect.enable_public_keys_storage - Gitlab::CurrentSettings.jira_connect_public_key_storage_enabled? end end diff --git a/app/controllers/jira_connect/subscriptions_controller.rb b/app/controllers/jira_connect/subscriptions_controller.rb index a206e7fbbd8..773ef2bddca 100644 --- a/app/controllers/jira_connect/subscriptions_controller.rb +++ b/app/controllers/jira_connect/subscriptions_controller.rb @@ -19,10 +19,6 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController p.style_src(*style_src_values) end - before_action do - push_frontend_feature_flag(:jira_connect_oauth, @user) - end - before_action :allow_rendering_in_iframe, only: :index before_action :verify_qsh_claim!, only: :index before_action :allow_self_managed_content_security_policy, only: :index @@ -34,7 +30,7 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController respond_to do |format| format.html format.json do - render json: JiraConnect::AppDataSerializer.new(@subscriptions, !!current_user).as_json + render json: JiraConnect::AppDataSerializer.new(@subscriptions).as_json end end end diff --git a/app/controllers/jira_connect/users_controller.rb b/app/controllers/jira_connect/users_controller.rb deleted file mode 100644 index a37c68de299..00000000000 --- a/app/controllers/jira_connect/users_controller.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -class JiraConnect::UsersController < ApplicationController - feature_category :integrations - - layout 'signup_onboarding' - - before_action :verify_return_to_url, only: [:show] - - def show - @jira_app_link = params.delete(:return_to) - end - - private - - def verify_return_to_url - return unless params[:return_to].present? - - params.delete(:return_to) unless Integrations::Jira.valid_jira_cloud_url?(params[:return_to]) - end -end diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb index 7211eebdb4b..d299613f498 100644 --- a/app/controllers/jwt_controller.rb +++ b/app/controllers/jwt_controller.rb @@ -8,7 +8,7 @@ class JwtController < ApplicationController # Add this before other actions, since we want to have the user or project prepend_before_action :auth_user, :authenticate_project_or_user - feature_category :authentication_and_authorization + feature_category :system_access # https://gitlab.com/gitlab-org/gitlab/-/issues/357037 urgency :low diff --git a/app/controllers/metrics_controller.rb b/app/controllers/metrics_controller.rb index bfd6181a940..3dfa8d7b11e 100644 --- a/app/controllers/metrics_controller.rb +++ b/app/controllers/metrics_controller.rb @@ -10,9 +10,10 @@ class MetricsController < ActionController::Base response = if Gitlab::Metrics.prometheus_metrics_enabled? metrics_service.metrics_text else - help_page = help_page_url('administration/monitoring/prometheus/gitlab_metrics', - anchor: 'gitlab-prometheus-metrics' - ) + help_page = help_page_url( + 'administration/monitoring/prometheus/gitlab_metrics', + anchor: 'gitlab-prometheus-metrics' + ) "# Metrics are disabled, see: #{help_page}\n" end diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index 3b78b997da1..2d5421f9f74 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -23,9 +23,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController set_index_vars end - def show - @created = get_created_session if Feature.disabled?('hash_oauth_secrets') - end + def show; end def create @application = Applications::CreateService.new(current_user, application_params).execute(request) @@ -33,20 +31,26 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController if @application.persisted? flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) - if Feature.enabled?('hash_oauth_secrets') - @created = true - render :show - else - set_created_session - - redirect_to oauth_application_url(@application) - end + @created = true + render :show else set_index_vars render :index end end + def renew + set_application + + @application.renew_secret + + if @application.save + render json: { secret: @application.plaintext_secret } + else + render json: { errors: @application.errors }, status: :unprocessable_entity + end + end + private def verify_user_oauth_applications_enabled diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb index 43bf895ea76..96a3fab7e1a 100644 --- a/app/controllers/oauth/authorizations_controller.rb +++ b/app/controllers/oauth/authorizations_controller.rb @@ -108,8 +108,10 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController end def dangerous_scopes? - doorkeeper_application&.includes_scope?(*::Gitlab::Auth::API_SCOPE, *::Gitlab::Auth::READ_API_SCOPE, - *::Gitlab::Auth::ADMIN_SCOPES, *::Gitlab::Auth::REPOSITORY_SCOPES, - *::Gitlab::Auth::REGISTRY_SCOPES) && !doorkeeper_application&.trusted? + doorkeeper_application&.includes_scope?( + *::Gitlab::Auth::API_SCOPE, *::Gitlab::Auth::READ_API_SCOPE, + *::Gitlab::Auth::ADMIN_SCOPES, *::Gitlab::Auth::REPOSITORY_SCOPES, + *::Gitlab::Auth::REGISTRY_SCOPES + ) && !doorkeeper_application&.trusted? end end diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb index 3f476c0d717..6fc2eb6bc45 100644 --- a/app/controllers/oauth/authorized_applications_controller.rb +++ b/app/controllers/oauth/authorized_applications_controller.rb @@ -20,7 +20,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio end redirect_to applications_profile_url, - status: :found, - notice: I18n.t(:notice, scope: [:doorkeeper, :flash, :authorized_applications, :destroy]) + status: :found, + notice: I18n.t(:notice, scope: [:doorkeeper, :flash, :authorized_applications, :destroy]) end end diff --git a/app/controllers/oauth/jira_dvcs/authorizations_controller.rb b/app/controllers/oauth/jira_dvcs/authorizations_controller.rb index 03921761f45..ba587944a36 100644 --- a/app/controllers/oauth/jira_dvcs/authorizations_controller.rb +++ b/app/controllers/oauth/jira_dvcs/authorizations_controller.rb @@ -8,6 +8,7 @@ class Oauth::JiraDvcs::AuthorizationsController < ApplicationController skip_before_action :authenticate_user! skip_before_action :verify_authenticity_token + before_action :reversible_end_of_life! before_action :validate_redirect_uri, only: :new feature_category :integrations @@ -16,10 +17,12 @@ class Oauth::JiraDvcs::AuthorizationsController < ApplicationController def new session[:redirect_uri] = params['redirect_uri'] - redirect_to oauth_authorization_path(client_id: params['client_id'], - response_type: 'code', - scope: normalize_scope(params['scope']), - redirect_uri: oauth_jira_dvcs_callback_url) + redirect_to oauth_authorization_path( + client_id: params['client_id'], + response_type: 'code', + scope: normalize_scope(params['scope']), + redirect_uri: oauth_jira_dvcs_callback_url + ) end # 2. Handle the callback call as we were a Github Enterprise instance client. @@ -53,6 +56,17 @@ class Oauth::JiraDvcs::AuthorizationsController < ApplicationController private + # The endpoints in this controller have been deprecated since 15.1. + # + # Due to uncertainty about the impact of a full removal in 16.0, all endpoints return `404` + # by default but we allow customers to toggle a flag to reverse this breaking change. + # See https://gitlab.com/gitlab-org/gitlab/-/issues/362168#note_1347692683. + # + # TODO Make the breaking change irreversible https://gitlab.com/gitlab-org/gitlab/-/issues/408148. + def reversible_end_of_life! + render_404 unless Feature.enabled?(:jira_dvcs_end_of_life_amnesty) + end + # When using the GitHub Enterprise connector in Jira we receive the "repo" scope, # this doesn't exist in GitLab but we can map it to our "api" scope. def normalize_scope(scope) diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 4046433f8ea..a2e0670d7e1 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -10,9 +10,9 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController after_action :verify_known_sign_in - protect_from_forgery except: [:cas3, :failure] + AuthHelper.saml_providers, with: :exception, prepend: true + protect_from_forgery except: [:failure] + AuthHelper.saml_providers, with: :exception, prepend: true - feature_category :authentication_and_authorization + feature_category :system_access def handle_omniauth omniauth_flow(Gitlab::Auth::OAuth) @@ -22,6 +22,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController alias_method provider, :handle_omniauth end + # overridden in EE + def openid_connect + handle_omniauth + end + # Extend the standard implementation to also increment # the number of failed sign in attempts def failure @@ -52,15 +57,6 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController redirect_unverified_saml_initiation end - def cas3 - ticket = params['ticket'] - if ticket - handle_service_ticket oauth['provider'], ticket - end - - handle_omniauth - end - def auth0 if oauth['uid'].blank? fail_auth0_login @@ -141,12 +137,6 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController redirect_to profile_account_path, notice: _('Authentication method updated') end - def handle_service_ticket(provider, ticket) - Gitlab::Auth::OAuth::Session.create provider, ticket - session[:service_tickets] ||= {} - session[:service_tickets][provider] = ticket - end - def build_auth_user(auth_user_class) auth_user_class.new(oauth) end @@ -178,7 +168,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController persist_accepted_terms_if_required(user) if new_user store_after_sign_up_path_for_user if intent_to_register? - sign_in_and_redirect_or_confirm_identity(user, auth_user, new_user) + sign_in_and_redirect_or_verify_identity(user, auth_user, new_user) end else fail_login(user) @@ -310,7 +300,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController end # overridden in EE - def sign_in_and_redirect_or_confirm_identity(user, _, _) + def sign_in_and_redirect_or_verify_identity(user, _, _) sign_in_and_redirect(user, event: :authentication) end end diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index 38cdb16c350..38839497fb6 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -12,7 +12,7 @@ class PasswordsController < Devise::PasswordsController before_action :check_password_authentication_available, only: [:create] before_action :throttle_reset, only: [:create] - feature_category :authentication_and_authorization + feature_category :system_access # rubocop: disable CodeReuse/ActiveRecord def edit diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb index cb8b2783000..eb64016379d 100644 --- a/app/controllers/profiles/accounts_controller.rb +++ b/app/controllers/profiles/accounts_controller.rb @@ -3,7 +3,7 @@ class Profiles::AccountsController < Profiles::ApplicationController include AuthHelper - feature_category :authentication_and_authorization + feature_category :system_access urgency :low, [:show] def show diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb index 2607ba7d404..5a86179b89f 100644 --- a/app/controllers/profiles/active_sessions_controller.rb +++ b/app/controllers/profiles/active_sessions_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Profiles::ActiveSessionsController < Profiles::ApplicationController - feature_category :authentication_and_authorization + feature_category :system_access def index @sessions = ActiveSession.list(current_user).reject(&:is_impersonated) diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb index ae757c30d1c..c4384c962d2 100644 --- a/app/controllers/profiles/chat_names_controller.rb +++ b/app/controllers/profiles/chat_names_controller.rb @@ -11,6 +11,7 @@ class Profiles::ChatNamesController < Profiles::ApplicationController end def new + @integration_name = integration_name end def create @@ -65,4 +66,15 @@ class Profiles::ChatNamesController < Profiles::ApplicationController def chat_names @chat_names ||= current_user.chat_names end + + def integration_name + return s_('Integrations|GitLab for Slack app') if slack_app_params? + + s_('Integrations|Mattermost slash commands') + end + + def slack_app_params? + chat_name_params[:team_id].start_with?('T') && + chat_name_params[:chat_id].start_with?('U', 'W') + end end diff --git a/app/controllers/profiles/saved_replies_controller.rb b/app/controllers/profiles/comment_templates_controller.rb index 5ac5d645efb..d6725c27f76 100644 --- a/app/controllers/profiles/saved_replies_controller.rb +++ b/app/controllers/profiles/comment_templates_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Profiles - class SavedRepliesController < Profiles::ApplicationController + class CommentTemplatesController < Profiles::ApplicationController feature_category :user_profile before_action do diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb index c88616b6d6c..28a57ef19f6 100644 --- a/app/controllers/profiles/emails_controller.rb +++ b/app/controllers/profiles/emails_controller.rb @@ -3,9 +3,9 @@ class Profiles::EmailsController < Profiles::ApplicationController before_action :find_email, only: [:destroy, :resend_confirmation_instructions] before_action -> { check_rate_limit!(:profile_add_new_email, scope: current_user, redirect_back: true) }, - only: [:create] + only: [:create] before_action -> { check_rate_limit!(:profile_resend_email_confirmation, scope: current_user, redirect_back: true) }, - only: [:resend_confirmation_instructions] + only: [:resend_confirmation_instructions] feature_category :user_profile urgency :low, [:index] diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb index 9323d266cd5..b663a75f04a 100644 --- a/app/controllers/profiles/notifications_controller.rb +++ b/app/controllers/profiles/notifications_controller.rb @@ -43,7 +43,10 @@ class Profiles::NotificationsController < Profiles::ApplicationController .preload_source_route projects = project_notifications.map(&:source) - ActiveRecord::Associations::Preloader.new.preload(projects, { namespace: [:route, :owner], group: [] }) + ActiveRecord::Associations::Preloader.new( + records: projects, + associations: { namespace: [:route, :owner], group: [] } + ).call Preloaders::UserMaxAccessLevelInProjectsPreloader.new(projects, current_user).execute project_notifications.select { |notification| current_user.can?(:read_project, notification.source) } diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb index 738c41207d5..7a0dfbbba0d 100644 --- a/app/controllers/profiles/passwords_controller.rb +++ b/app/controllers/profiles/passwords_controller.rb @@ -11,7 +11,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController layout :determine_layout - feature_category :authentication_and_authorization + feature_category :system_access def new end diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb index 1663aa61f62..4b6e2f768fa 100644 --- a/app/controllers/profiles/personal_access_tokens_controller.rb +++ b/app/controllers/profiles/personal_access_tokens_controller.rb @@ -3,7 +3,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController include RenderAccessTokens - feature_category :authentication_and_authorization + feature_category :system_access before_action :check_personal_access_tokens_enabled @@ -25,7 +25,10 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController def create result = ::PersonalAccessTokens::CreateService.new( - current_user: current_user, target_user: current_user, params: personal_access_token_params + current_user: current_user, + target_user: current_user, + params: personal_access_token_params, + concatenate_errors: false ).execute @personal_access_token = result.payload[:personal_access_token] diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb index 7786bad4251..a5a2cbf3733 100644 --- a/app/controllers/profiles/preferences_controller.rb +++ b/app/controllers/profiles/preferences_controller.rb @@ -37,7 +37,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController end def preferences_param_names - [ + preferences_param_names = [ :color_scheme_id, :diffs_deletion_color, :diffs_addition_color, @@ -48,7 +48,6 @@ class Profiles::PreferencesController < Profiles::ApplicationController :first_day_of_week, :preferred_language, :time_display_relative, - :time_format_in_24h, :show_whitespace_in_diffs, :view_diffs_file_by_file, :tab_width, @@ -57,9 +56,10 @@ class Profiles::PreferencesController < Profiles::ApplicationController :render_whitespace_in_code, :markdown_surround_selection, :markdown_automatic_lists, - :use_legacy_web_ide, :use_new_navigation ] + preferences_param_names << :enabled_following if ::Feature.enabled?(:disable_follow_users, user) + preferences_param_names end end diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index aded295bfab..bc6e67a3a7d 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -8,11 +8,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController helper_method :current_password_required? - before_action do - push_frontend_feature_flag(:webauthn) - end - - feature_category :authentication_and_authorization + feature_category :system_access def show setup_show_page @@ -41,32 +37,12 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController @error = { message: _('Invalid pin code.') } @qr_code = build_qr_code @account_string = account_string - - if Feature.enabled?(:webauthn) - setup_webauthn_registration - else - setup_u2f_registration - end + setup_webauthn_registration render 'show' end end - # A U2F (universal 2nd factor) device's information is stored after successful - # registration, which is then used while 2FA authentication is taking place. - def create_u2f - @u2f_registration = U2fRegistration.register(current_user, u2f_app_id, device_registration_params, session[:challenges]) - - if @u2f_registration.persisted? - session.delete(:challenges) - redirect_to profile_two_factor_auth_path, notice: s_("Your U2F device was registered!") - else - @qr_code = build_qr_code - setup_u2f_registration - render :show - end - end - def create_webauthn @webauthn_registration = Webauthn::RegisterService.new(current_user, device_registration_params, session[:challenge]).execute @@ -175,22 +151,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController Gitlab.config.gitlab.host end - # Setup in preparation of communication with a U2F (universal 2nd factor) device - # Actual communication is performed using a Javascript API - def setup_u2f_registration - @u2f_registration ||= U2fRegistration.new - @registrations = u2f_registrations - u2f = U2F::U2F.new(u2f_app_id) - - registration_requests = u2f.registration_requests - sign_requests = u2f.authentication_requests(current_user.u2f_registrations.map(&:key_handle)) - session[:challenges] = registration_requests.map(&:challenge) - - gon.push(u2f: { challenges: session[:challenges], app_id: u2f_app_id, - register_requests: registration_requests, - sign_requests: sign_requests }) - end - def device_registration_params params.require(:device_registration).permit(:device_response, :name) end @@ -209,18 +169,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController gon.push(webauthn: { options: options, app_id: u2f_app_id }) end - # Adds delete path to u2f registrations - # to reduce logic in view template - def u2f_registrations - current_user.u2f_registrations.map do |u2f_registration| - { - name: u2f_registration.name, - created_at: u2f_registration.created_at, - delete_path: profile_u2f_registration_path(u2f_registration) - } - end - end - def webauthn_registrations current_user.webauthn_registrations.map do |webauthn_registration| { @@ -275,10 +223,6 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController @qr_code = build_qr_code @account_string = account_string - if Feature.enabled?(:webauthn) - setup_webauthn_registration - else - setup_u2f_registration - end + setup_webauthn_registration end end diff --git a/app/controllers/profiles/u2f_registrations_controller.rb b/app/controllers/profiles/u2f_registrations_controller.rb deleted file mode 100644 index 32ca303e722..00000000000 --- a/app/controllers/profiles/u2f_registrations_controller.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -class Profiles::U2fRegistrationsController < Profiles::ApplicationController - feature_category :authentication_and_authorization - - def destroy - u2f_registration = current_user.u2f_registrations.find(params[:id]) - u2f_registration.destroy - redirect_to profile_two_factor_auth_path, status: :found, notice: _("Successfully deleted U2F device.") - end -end diff --git a/app/controllers/profiles/webauthn_registrations_controller.rb b/app/controllers/profiles/webauthn_registrations_controller.rb index a4a6d84f1ae..345d7bdbca8 100644 --- a/app/controllers/profiles/webauthn_registrations_controller.rb +++ b/app/controllers/profiles/webauthn_registrations_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Profiles::WebauthnRegistrationsController < Profiles::ApplicationController - feature_category :authentication_and_authorization + feature_category :system_access def destroy webauthn_registration = current_user.webauthn_registrations.find(params[:id]) diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 45b274fc920..da15b393e6c 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -10,14 +10,11 @@ class ProfilesController < Profiles::ApplicationController check_rate_limit!(:profile_update_username, scope: current_user) end skip_before_action :require_email, only: [:show, :update] - before_action do - push_frontend_feature_flag(:webauthn) - end feature_category :user_profile, [:show, :update, :reset_incoming_email_token, :reset_feed_token, :reset_static_object_token, :update_username] - feature_category :authentication_and_authorization, [:audit_log] + feature_category :system_access, [:audit_log] urgency :low, [:show, :update] def show @@ -133,6 +130,7 @@ class ProfilesController < Profiles::ApplicationController :organization, :private_profile, :include_private_contributions, + :achievements_enabled, :timezone, :job_title, :pronouns, diff --git a/app/controllers/projects/airflow/dags_controller.rb b/app/controllers/projects/airflow/dags_controller.rb deleted file mode 100644 index 9d1f0b0d63b..00000000000 --- a/app/controllers/projects/airflow/dags_controller.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module Projects - module Airflow - class DagsController < ::Projects::ApplicationController - before_action :check_feature_flag - before_action :authorize_read_airflow_dags! - - feature_category :dataops - - MAX_DAGS_PER_PAGE = 15 - def index - page = params[:page].to_i - page = 1 if page <= 0 - - @dags = ::Airflow::Dags.by_project_id(@project.id) - - return unless @dags.any? - - @dags = @dags.page(page).per(MAX_DAGS_PER_PAGE) - return redirect_to(url_for(page: @dags.total_pages)) if @dags.out_of_range? - - @pagination = { - page: page, - is_last_page: @dags.last_page?, - per_page: MAX_DAGS_PER_PAGE, - total_items: @dags.total_count - } - end - - private - - def check_feature_flag - render_404 unless Feature.enabled?(:airflow_dags, @project) - end - end - end -end diff --git a/app/controllers/projects/analytics/cycle_analytics/stages_controller.rb b/app/controllers/projects/analytics/cycle_analytics/stages_controller.rb index a61b774f9c8..6d48aa765fb 100644 --- a/app/controllers/projects/analytics/cycle_analytics/stages_controller.rb +++ b/app/controllers/projects/analytics/cycle_analytics/stages_controller.rb @@ -6,7 +6,7 @@ class Projects::Analytics::CycleAnalytics::StagesController < Projects::Applicat respond_to :json - feature_category :planning_analytics + feature_category :team_planning before_action :authorize_read_cycle_analytics! before_action :only_default_value_stream_is_allowed! diff --git a/app/controllers/projects/analytics/cycle_analytics/summary_controller.rb b/app/controllers/projects/analytics/cycle_analytics/summary_controller.rb index 69327feeb02..1f58b97867f 100644 --- a/app/controllers/projects/analytics/cycle_analytics/summary_controller.rb +++ b/app/controllers/projects/analytics/cycle_analytics/summary_controller.rb @@ -1,11 +1,12 @@ # frozen_string_literal: true class Projects::Analytics::CycleAnalytics::SummaryController < Projects::ApplicationController + extend ::Gitlab::Utils::Override include CycleAnalyticsParams respond_to :json - feature_category :planning_analytics + feature_category :team_planning before_action :authorize_read_cycle_analytics! @@ -17,6 +18,10 @@ class Projects::Analytics::CycleAnalytics::SummaryController < Projects::Applica private + def namespace + @project.project_namespace + end + def project_level @project_level ||= Analytics::CycleAnalytics::ProjectLevel.new(project: @project, options: options(allowed_params)) end diff --git a/app/controllers/projects/analytics/cycle_analytics/value_streams_controller.rb b/app/controllers/projects/analytics/cycle_analytics/value_streams_controller.rb index f58730f1d33..bf6c667b87b 100644 --- a/app/controllers/projects/analytics/cycle_analytics/value_streams_controller.rb +++ b/app/controllers/projects/analytics/cycle_analytics/value_streams_controller.rb @@ -5,7 +5,7 @@ class Projects::Analytics::CycleAnalytics::ValueStreamsController < Projects::Ap respond_to :json - feature_category :planning_analytics + feature_category :team_planning urgency :low private diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 5f8060ad756..b5b023a4d64 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -19,17 +19,15 @@ class Projects::ArtifactsController < Projects::ApplicationController before_action :validate_artifacts!, except: [:index, :download, :raw, :destroy] before_action :entry, only: [:external_file, :file] + before_action only: :index do + push_frontend_feature_flag(:ci_job_artifact_bulk_destroy, @project) + end + MAX_PER_PAGE = 20 feature_category :build_artifacts - def index - # Loading artifacts is very expensive in projects with a lot of artifacts. - # This feature flag prevents a DOS attack vector. - # It should be removed only after resolving the underlying performance - # issues: https://gitlab.com/gitlab-org/gitlab/issues/32281 - return head :no_content unless Feature.enabled?(:artifacts_management_page, @project) - end + def index; end def destroy notice = if artifact.destroy diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb index 70d9b524e4d..5db7609e07a 100644 --- a/app/controllers/projects/avatars_controller.rb +++ b/app/controllers/projects/avatars_controller.rb @@ -3,8 +3,6 @@ class Projects::AvatarsController < Projects::ApplicationController include SendsBlob - skip_before_action :default_cache_headers, only: :show - before_action :authorize_admin_project!, only: [:destroy] feature_category :projects diff --git a/app/controllers/projects/aws/base_controller.rb b/app/controllers/projects/aws/base_controller.rb new file mode 100644 index 00000000000..e183c617a5e --- /dev/null +++ b/app/controllers/projects/aws/base_controller.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Projects + module Aws + class BaseController < Projects::ApplicationController + feature_category :five_minute_production_app + urgency :low + + before_action :admin_project_aws! + before_action :feature_flag_enabled! + + def admin_project_aws! + return if can?(current_user, :admin_project_aws, project) + + track_event(:error_invalid_user) + access_denied! + end + + def feature_flag_enabled! + return if Feature.enabled?(:cloudseed_aws, current_user) + return if Feature.enabled?(:cloudseed_aws, project.group) + return if Feature.enabled?(:cloudseed_aws, project) + + track_event(:error_feature_flag_not_enabled) + access_denied! + end + + def track_event(action, label = nil) + Gitlab::Tracking.event( + self.class.name, + action.to_s, + label: label, + project: project, + user: current_user + ) + end + end + end +end diff --git a/app/controllers/projects/aws/configuration_controller.rb b/app/controllers/projects/aws/configuration_controller.rb new file mode 100644 index 00000000000..42479d01008 --- /dev/null +++ b/app/controllers/projects/aws/configuration_controller.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Projects + module Aws + class ConfigurationController < Projects::Aws::BaseController + def index + js_data = {} + @js_data = Gitlab::Json.dump(js_data) + track_event(:render_page) + end + end + end +end diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb index dbbffc4c283..372da64cdfa 100644 --- a/app/controllers/projects/badges_controller.rb +++ b/app/controllers/projects/badges_controller.rb @@ -40,6 +40,7 @@ class Projects::BadgesController < Projects::ApplicationController .new(project, current_user, opts: { key_text: params[:key_text], key_width: params[:key_width], + value_width: params[:value_width], order_by: params[:order_by] }) diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index cfff281604e..bb1b8760c42 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -8,28 +8,54 @@ class Projects::BlameController < Projects::ApplicationController before_action :require_non_empty_project before_action :assign_ref_vars before_action :authorize_read_code! + before_action :load_blob feature_category :source_code_management urgency :low, [:show] def show + load_environment + load_blame + end + + def streaming + show + render action: 'show' + end + + def page + load_environment + load_blame + + render partial: 'page' + end + + private + + def load_blob @blob = @repository.blob_at(@commit.id, @path) - unless @blob - return redirect_to_tree_root_for_missing_path(@project, @ref, @path) - end + return if @blob + + redirect_to_tree_root_for_missing_path(@project, @ref, @path) + end + def load_environment environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit } environment_params[:find_latest] = true @environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last + end - blame_service = Projects::BlameService.new(@blob, @commit, params.permit(:page, :no_pagination)) - - @blame = Gitlab::View::Presenter::Factory.new(blame_service.blame, project: @project, path: @path, page: blame_service.page).fabricate! + def load_blame + @blame_mode = Gitlab::Git::BlameMode.new(@commit.project, blame_params) + @blame_pagination = Gitlab::Git::BlamePagination.new(@blob, @blame_mode, blame_params) - @blame_pagination = blame_service.pagination + blame = Gitlab::Blame.new(@blob, @commit, range: @blame_pagination.blame_range) + @blame = Gitlab::View::Presenter::Factory.new(blame, project: @project, path: @path, page: @blame_pagination.page).fabricate! + end - @blame_per_page = blame_service.per_page + def blame_params + params.permit(:page, :no_pagination, :streaming) end end diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index b71af82535a..727a4e0251d 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -10,7 +10,7 @@ class Projects::BlobController < Projects::ApplicationController include RedirectsForMissingPathOnTree include SourcegraphDecorator include DiffHelper - include RedisTracking + include ProductAnalyticsTracking extend ::Gitlab::Utils::Override prepend_before_action :authenticate_user!, only: [:edit] @@ -38,14 +38,19 @@ class Projects::BlobController < Projects::ApplicationController before_action :validate_diff_params, only: :diff before_action :set_last_commit_sha, only: [:edit, :update] - track_redis_hll_event :create, :update, name: 'g_edit_by_sfe' + track_event :create, :update, + name: 'g_edit_by_sfe', + action: 'perform_sfe_action', + label: 'usage_activity_by_stage_monthly.create.action_monthly_active_users_sfe_edit', + destinations: [:redis_hll, :snowplow] feature_category :source_code_management urgency :low, [:create, :show, :edit, :update, :diff] before_action do push_frontend_feature_flag(:highlight_js, @project) - push_frontend_feature_flag(:file_line_blame, @project) + push_frontend_feature_flag(:synchronize_fork, @project&.fork_source) + push_frontend_feature_flag(:explain_code_chat, current_user) push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) end @@ -54,10 +59,13 @@ class Projects::BlobController < Projects::ApplicationController end def create - create_commit(Files::CreateService, success_notice: _("The file has been successfully created."), - success_path: -> { project_blob_path(@project, File.join(@branch_name, @file_path)) }, - failure_view: :new, - failure_path: project_new_blob_path(@project, @ref)) + create_commit( + Files::CreateService, + success_notice: _("The file has been successfully created."), + success_path: -> { project_blob_path(@project, File.join(@branch_name, @file_path)) }, + failure_view: :new, + failure_path: project_new_blob_path(@project, @ref) + ) end def show @@ -87,11 +95,14 @@ class Projects::BlobController < Projects::ApplicationController def update @path = params[:file_path] if params[:file_path].present? - create_commit(Files::UpdateService, success_path: -> { after_edit_path }, - failure_view: :edit, - failure_path: project_blob_path(@project, @id)) + create_commit( + Files::UpdateService, success_path: -> { after_edit_path }, + failure_view: :edit, + failure_path: project_blob_path(@project, @id) + ) rescue Files::UpdateService::FileChangedError @conflict = true + @different_project = different_project? render :edit end @@ -107,9 +118,12 @@ class Projects::BlobController < Projects::ApplicationController end def destroy - create_commit(Files::DeleteService, success_notice: _("The file has been successfully deleted."), - success_path: -> { after_delete_path }, - failure_path: project_blob_path(@project, @id)) + create_commit( + Files::DeleteService, + success_notice: _("The file has been successfully deleted."), + success_path: -> { after_delete_path }, + failure_path: project_blob_path(@project, @id) + ) end def diff @@ -318,6 +332,12 @@ class Projects::BlobController < Projects::ApplicationController file = file.cdn_enabled_url(request.remote_ip) if file.respond_to?(:cdn_enabled_url) file.url end + + alias_method :tracking_project_source, :project + + def tracking_namespace_source + project&.namespace + end end Projects::BlobController.prepend_mod diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index f19f143816f..1e17dd586c7 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -98,7 +98,7 @@ class Projects::BranchesController < Projects::ApplicationController if success render json: { name: branch_name, url: project_tree_url(@project, branch_name) } else - render json: result[:messsage], status: :unprocessable_entity + render json: result[:message], status: :unprocessable_entity end end end diff --git a/app/controllers/projects/ci/lints_controller.rb b/app/controllers/projects/ci/lints_controller.rb index 7ef5016ac00..6762f1c7110 100644 --- a/app/controllers/projects/ci/lints_controller.rb +++ b/app/controllers/projects/ci/lints_controller.rb @@ -3,7 +3,7 @@ class Projects::Ci::LintsController < Projects::ApplicationController before_action :authorize_create_pipeline! - feature_category :pipeline_authoring + feature_category :pipeline_composition respond_to :json, only: [:create] urgency :low, [:create] diff --git a/app/controllers/projects/ci/pipeline_editor_controller.rb b/app/controllers/projects/ci/pipeline_editor_controller.rb index 3a2bc445737..d874c60daec 100644 --- a/app/controllers/projects/ci/pipeline_editor_controller.rb +++ b/app/controllers/projects/ci/pipeline_editor_controller.rb @@ -4,9 +4,10 @@ class Projects::Ci::PipelineEditorController < Projects::ApplicationController before_action :check_can_collaborate! before_action do push_frontend_feature_flag(:ci_job_assistant_drawer, @project) + push_frontend_feature_flag(:ai_ci_config_generator, @project) end - feature_category :pipeline_authoring + feature_category :pipeline_composition urgency :low, [:show] diff --git a/app/controllers/projects/ci/prometheus_metrics/histograms_controller.rb b/app/controllers/projects/ci/prometheus_metrics/histograms_controller.rb index 003441d4b91..72a07269d79 100644 --- a/app/controllers/projects/ci/prometheus_metrics/histograms_controller.rb +++ b/app/controllers/projects/ci/prometheus_metrics/histograms_controller.rb @@ -4,7 +4,7 @@ module Projects module Ci module PrometheusMetrics class HistogramsController < Projects::ApplicationController - feature_category :pipeline_authoring + feature_category :pipeline_composition respond_to :json, only: [:create] diff --git a/app/controllers/projects/cluster_agents_controller.rb b/app/controllers/projects/cluster_agents_controller.rb index 3f759e5c18c..bd58bd3e470 100644 --- a/app/controllers/projects/cluster_agents_controller.rb +++ b/app/controllers/projects/cluster_agents_controller.rb @@ -1,22 +1,17 @@ # frozen_string_literal: true class Projects::ClusterAgentsController < Projects::ApplicationController - before_action :authorize_can_read_cluster_agent! + include KasCookie - feature_category :kubernetes_management + before_action :authorize_read_cluster_agent! + before_action :set_kas_cookie, only: [:show], if: -> { current_user } + + feature_category :deployment_management urgency :low def show @agent_name = params[:name] end - - private - - def authorize_can_read_cluster_agent! - return if can?(current_user, :read_cluster, project) - - access_denied! - end end Projects::ClusterAgentsController.prepend_mod_with('Projects::ClusterAgentsController') diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 252b203b38a..8aca6a3fd5b 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -48,7 +48,11 @@ class Projects::CommitController < Projects::ApplicationController end def diff_files - render template: 'projects/commit/diff_files', layout: false, locals: { diffs: @diffs, environment: @environment } + respond_to do |format| + format.html do + render template: 'projects/commit/diff_files', layout: false, locals: { diffs: @diffs, environment: @environment } + end + end end # rubocop: disable CodeReuse/ActiveRecord @@ -115,8 +119,12 @@ class Projects::CommitController < Projects::ApplicationController @branch_name = create_new_branch? ? @commit.revert_branch_name : @start_branch - create_commit(Commits::RevertService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully reverted.", - success_path: -> { successful_change_path(@project) }, failure_path: failed_change_path) + create_commit( + Commits::RevertService, + success_notice: "The #{@commit.change_type_title(current_user)} has been successfully reverted.", + success_path: -> { successful_change_path(@project) }, + failure_path: failed_change_path + ) end def cherry_pick @@ -131,10 +139,13 @@ class Projects::CommitController < Projects::ApplicationController @branch_name = create_new_branch? ? @commit.cherry_pick_branch_name : @start_branch - create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title(current_user)} has been successfully cherry-picked into #{@branch_name}.", - success_path: -> { successful_change_path(target_project) }, - failure_path: failed_change_path, - target_project: target_project) + create_commit( + Commits::CherryPickService, + success_notice: "The #{@commit.change_type_title(current_user)} has been successfully cherry-picked into #{@branch_name}.", + success_path: -> { successful_change_path(target_project) }, + failure_path: failed_change_path, + target_project: target_project + ) end private diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index 3acc71d5dd3..94cd324f312 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -7,7 +7,6 @@ class Projects::CommitsController < Projects::ApplicationController include RendersCommits COMMITS_DEFAULT_LIMIT = 40 - prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) } around_action :allow_gitaly_ref_name_caching before_action :require_non_empty_project @@ -77,15 +76,22 @@ class Projects::CommitsController < Projects::ApplicationController # fully_qualified_ref is available in some situations from ExtractsRef ref = @fully_qualified_ref || @ref + @commits = if search.present? @repository.find_commits_by_message(search, ref, @path, @limit, @offset) - elsif author.present? - @repository.commits(ref, author: author, path: @path, limit: @limit, offset: @offset) else - @repository.commits(ref, path: @path, limit: @limit, offset: @offset) + options = { + path: @path, + limit: @limit, + offset: @offset + } + options[:author] = author if author.present? + + @repository.commits(ref, **options) end + @commits.load_tags @commits.each(&:lazy_author) # preload authors @commits = @commits.with_markdown_cache.with_latest_pipeline(ref) diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 43b4cdbe9a8..d41a7e4f2df 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -11,7 +11,7 @@ module Projects before_action :authorize_read_issue!, only: [:issue, :production] before_action :authorize_read_merge_request!, only: [:code, :review] - feature_category :planning_analytics + feature_category :team_planning urgency :low def issue diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index 9fe44659250..10dd18c0c86 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -6,22 +6,28 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController include CycleAnalyticsParams include GracefulTimeoutHandling include ProductAnalyticsTracking + include Gitlab::Utils::StrongMemoize extend ::Gitlab::Utils::Override before_action :authorize_read_cycle_analytics! - before_action :load_value_stream, only: :show - track_custom_event :show, + track_event :show, name: 'p_analytics_valuestream', action: 'perform_analytics_usage_action', label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', destinations: %i[redis_hll snowplow] - feature_category :planning_analytics + feature_category :team_planning urgency :low before_action do push_licensed_feature(:cycle_analytics_for_groups) if project.licensed_feature_available?(:cycle_analytics_for_groups) + push_licensed_feature(:group_level_analytics_dashboard) if project.licensed_feature_available?(:group_level_analytics_dashboard) + + if project.licensed_feature_available?(:cycle_analytics_for_projects) + push_licensed_feature(:cycle_analytics_for_projects) + push_frontend_feature_flag(:vsa_group_and_project_parity, @project) + end end def show @@ -44,12 +50,13 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController override :all_cycle_analytics_params def all_cycle_analytics_params - super.merge({ project: @project, value_stream: @value_stream }) + super.merge({ value_stream: value_stream }) end - def load_value_stream - @value_stream = Analytics::CycleAnalytics::ValueStream.build_default_value_stream(@project.project_namespace) + def value_stream + Analytics::CycleAnalytics::ValueStream.build_default_value_stream(namespace) end + strong_memoize_attr :value_stream def cycle_analytics_json { @@ -59,6 +66,10 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController } end + def namespace + @project.project_namespace + end + def tracking_namespace_source project.namespace end @@ -67,3 +78,5 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController project end end + +Projects::CycleAnalyticsController.prepend_mod diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index 22a42d22914..9cdbd2a30f6 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -82,7 +82,7 @@ class Projects::DeployKeysController < Projects::ApplicationController def create_params create_params = params.require(:deploy_key) - .permit(:key, :title, deploy_keys_projects_attributes: [:can_push]) + .permit(:key, :title, :expires_at, deploy_keys_projects_attributes: [:can_push]) create_params.dig(:deploy_keys_projects_attributes, '0')&.merge!(project_id: @project.id) create_params end diff --git a/app/controllers/projects/design_management/designs/raw_images_controller.rb b/app/controllers/projects/design_management/designs/raw_images_controller.rb index beb7e9d294b..ea406d2f2ef 100644 --- a/app/controllers/projects/design_management/designs/raw_images_controller.rb +++ b/app/controllers/projects/design_management/designs/raw_images_controller.rb @@ -7,8 +7,6 @@ module Projects class RawImagesController < Projects::DesignManagement::DesignsController include SendsBlob - skip_before_action :default_cache_headers, only: :show - def show blob = design_repository.blob_at(ref, design.full_path) diff --git a/app/controllers/projects/design_management/designs/resized_image_controller.rb b/app/controllers/projects/design_management/designs/resized_image_controller.rb index 6bf304419e1..a09d8a73892 100644 --- a/app/controllers/projects/design_management/designs/resized_image_controller.rb +++ b/app/controllers/projects/design_management/designs/resized_image_controller.rb @@ -10,8 +10,6 @@ module Projects before_action :validate_size! before_action :validate_sha! - skip_before_action :default_cache_headers, only: :show - def show relation = design.actions relation = relation.up_to_version(version) if version diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 9a88a8160b6..f91ec55573d 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -9,6 +9,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController include MetricsDashboard include ProductAnalyticsTracking + include KasCookie layout 'project' @@ -20,6 +21,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController push_frontend_feature_flag(:environment_details_vue, @project) end + before_action only: [:index] do + push_frontend_feature_flag(:kas_user_access_project, @project) + end + before_action :authorize_read_environment!, except: [:metrics, :additional_metrics, :metrics_dashboard, :metrics_redirect] before_action :authorize_create_environment!, only: [:new, :create] before_action :authorize_stop_environment!, only: [:stop] @@ -28,19 +33,11 @@ class Projects::EnvironmentsController < Projects::ApplicationController before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize, :metrics, :cancel_auto_stop] before_action :verify_api_request!, only: :terminal_websocket_authorize before_action :expire_etag_cache, only: [:index], unless: -> { request.format.json? } + before_action :set_kas_cookie, only: [:index], if: -> { current_user } after_action :expire_etag_cache, only: [:cancel_auto_stop] - track_event :index, - :folder, - :show, - :new, - :edit, - :create, - :update, - :stop, - :cancel_auto_stop, - :terminal, - name: 'users_visiting_environments_pages' + track_event :index, :folder, :show, :new, :edit, :create, :update, :stop, :cancel_auto_stop, :terminal, + name: 'users_visiting_environments_pages' feature_category :continuous_delivery urgency :low @@ -179,10 +176,14 @@ class Projects::EnvironmentsController < Projects::ApplicationController end def metrics_redirect + return not_found if Feature.enabled?(:remove_monitor_metrics) + redirect_to project_metrics_dashboard_path(project) end def metrics + return not_found if Feature.enabled?(:remove_monitor_metrics) + respond_to do |format| format.html do redirect_to project_metrics_dashboard_path(project, environment: environment) @@ -198,6 +199,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController end def additional_metrics + return not_found if Feature.enabled?(:remove_monitor_metrics) + respond_to do |format| format.json do additional_metrics = environment.additional_metrics(*metrics_params) || {} @@ -255,11 +258,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController def search_environments(type: nil) search = params[:search] if params[:search] && params[:search].length >= MIN_SEARCH_LENGTH - @search_environments ||= - Environments::EnvironmentsFinder.new(project, - current_user, - type: type, - search: search).execute + @search_environments ||= Environments::EnvironmentsFinder.new(project, current_user, type: type, search: search).execute end def metrics_params @@ -301,16 +300,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController def authorize_update_environment! access_denied! unless can?(current_user, :update_environment, environment) end - - def append_info_to_payload(payload) - super - - return unless Feature.enabled?(:environments_search_logging) && params[:search].present? - - # Merging to :metadata will ensure these are logged as top level keys - payload[:metadata] ||= {} - payload[:metadata]['meta.environment.search'] = params[:search] - end end Projects::EnvironmentsController.prepend_mod_with('Projects::EnvironmentsController') diff --git a/app/controllers/projects/error_tracking_controller.rb b/app/controllers/projects/error_tracking_controller.rb index d2e36ef5496..d70ee0fabea 100644 --- a/app/controllers/projects/error_tracking_controller.rb +++ b/app/controllers/projects/error_tracking_controller.rb @@ -74,8 +74,7 @@ class Projects::ErrorTrackingController < Projects::ErrorTracking::BaseControlle def render_errors(result) unless result[:status] == :success - render json: { message: result[:message] }, - status: result[:http_status] || :bad_request + render json: { message: result[:message] }, status: result[:http_status] || :bad_request end end diff --git a/app/controllers/projects/feature_flags_controller.rb b/app/controllers/projects/feature_flags_controller.rb index 16392775c09..83923965a45 100644 --- a/app/controllers/projects/feature_flags_controller.rb +++ b/app/controllers/projects/feature_flags_controller.rb @@ -97,23 +97,45 @@ class Projects::FeatureFlagsController < Projects::ApplicationController end def create_params - params.require(:operations_feature_flag) - .permit(:name, :description, :active, :version, - scopes_attributes: [:environment_scope, :active, - strategies: [:name, parameters: [:groupId, :percentage, :userIds]]], - strategies_attributes: [:name, :user_list_id, - parameters: [:groupId, :percentage, :userIds, :rollout, :stickiness], - scopes_attributes: [:environment_scope]]) + params.require(:operations_feature_flag).permit( + :name, + :description, + :active, + :version, + scopes_attributes: [ + :environment_scope, :active, + strategies: [:name, parameters: [:groupId, :percentage, :userIds]] + ], + strategies_attributes: [ + :name, + :user_list_id, + parameters: [:groupId, :percentage, :userIds, :rollout, :stickiness], + scopes_attributes: [:environment_scope] + ] + ) end def update_params - params.require(:operations_feature_flag) - .permit(:name, :description, :active, - scopes_attributes: [:id, :environment_scope, :active, :_destroy, - strategies: [:name, parameters: [:groupId, :percentage, :userIds]]], - strategies_attributes: [:id, :name, :user_list_id, :_destroy, - parameters: [:groupId, :percentage, :userIds, :rollout, :stickiness], - scopes_attributes: [:id, :environment_scope, :_destroy]]) + params.require(:operations_feature_flag).permit( + :name, + :description, + :active, + scopes_attributes: [ + :id, + :environment_scope, + :active, + :_destroy, + strategies: [:name, parameters: [:groupId, :percentage, :userIds]] + ], + strategies_attributes: [ + :id, + :name, + :user_list_id, + :_destroy, + parameters: [:groupId, :percentage, :userIds, :rollout, :stickiness], + scopes_attributes: [:id, :environment_scope, :_destroy] + ] + ) end def feature_flag_json(feature_flag) @@ -144,7 +166,6 @@ class Projects::FeatureFlagsController < Projects::ApplicationController end def render_error_json(messages, status = :bad_request) - render json: { message: messages }, - status: status + render json: { message: messages }, status: status end end diff --git a/app/controllers/projects/google_cloud/base_controller.rb b/app/controllers/projects/google_cloud/base_controller.rb index dfb73821b0f..7eccc0c1c77 100644 --- a/app/controllers/projects/google_cloud/base_controller.rb +++ b/app/controllers/projects/google_cloud/base_controller.rb @@ -45,8 +45,8 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController return_url = project_google_cloud_configuration_path(project) state = generate_session_key_redirect(request.url, return_url) @authorize_url = GoogleApi::CloudPlatform::Client.new(nil, - callback_google_api_auth_url, - state: state).authorize_url + callback_google_api_auth_url, + state: state).authorize_url redirect_to @authorize_url end diff --git a/app/controllers/projects/google_cloud/configuration_controller.rb b/app/controllers/projects/google_cloud/configuration_controller.rb index e109ab95d39..d35b2d54c53 100644 --- a/app/controllers/projects/google_cloud/configuration_controller.rb +++ b/app/controllers/projects/google_cloud/configuration_controller.rb @@ -10,7 +10,8 @@ module Projects databasesUrl: project_google_cloud_databases_path(project), serviceAccounts: ::GoogleCloud::ServiceAccountsService.new(project).find_for_project, createServiceAccountUrl: project_google_cloud_service_accounts_path(project), - emptyIllustrationUrl: ActionController::Base.helpers.image_path('illustrations/pipelines_empty.svg'), + emptyIllustrationUrl: + ActionController::Base.helpers.image_path('illustrations/empty-state/empty-pipeline-md.svg'), configureGcpRegionsUrl: project_google_cloud_gcp_regions_path(project), gcpRegions: gcp_regions, revokeOauthUrl: revoke_oauth_url diff --git a/app/controllers/projects/google_cloud/databases_controller.rb b/app/controllers/projects/google_cloud/databases_controller.rb index 9c20f10809c..ea79efd9f4f 100644 --- a/app/controllers/projects/google_cloud/databases_controller.rb +++ b/app/controllers/projects/google_cloud/databases_controller.rb @@ -15,7 +15,8 @@ module Projects cloudsqlMysqlUrl: new_project_google_cloud_database_path(project, :mysql), cloudsqlSqlserverUrl: new_project_google_cloud_database_path(project, :sqlserver), cloudsqlInstances: ::GoogleCloud::GetCloudsqlInstancesService.new(project).execute, - emptyIllustrationUrl: ActionController::Base.helpers.image_path('illustrations/pipelines_empty.svg') + emptyIllustrationUrl: + ActionController::Base.helpers.image_path('illustrations/empty-state/empty-pipeline-md.svg') } @js_data = Gitlab::Json.dump(js_data) diff --git a/app/controllers/projects/grafana_api_controller.rb b/app/controllers/projects/grafana_api_controller.rb index 9cd511f6a11..2cc6c6c35ba 100644 --- a/app/controllers/projects/grafana_api_controller.rb +++ b/app/controllers/projects/grafana_api_controller.rb @@ -10,6 +10,8 @@ class Projects::GrafanaApiController < Projects::ApplicationController urgency :low def proxy + return not_found if Feature.enabled?(:remove_monitor_metrics) + result = ::Grafana::ProxyService.new( project, params[:datasource_id], diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index d072381933a..e73e2a38149 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -9,7 +9,7 @@ class Projects::GraphsController < Projects::ApplicationController before_action :assign_ref_vars before_action :authorize_read_repository_graphs! - track_custom_event :charts, + track_event :charts, name: 'p_analytics_repo', action: 'perform_analytics_usage_action', label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 22b6bf6faf0..570fe74f31f 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -4,8 +4,8 @@ class Projects::HooksController < Projects::ApplicationController include ::WebHooks::HookActions # Authorize - before_action :authorize_admin_project! - before_action :hook_logs, only: :edit + before_action :authorize_admin_project!, except: :destroy + before_action :authorize_destroy_project_hook!, only: :destroy before_action -> { check_rate_limit!(:project_testing_hook, scope: [@project, current_user]) }, only: :test respond_to :html @@ -34,11 +34,11 @@ class Projects::HooksController < Projects::ApplicationController @hook ||= @project.hooks.find(params[:id]) end - def hook_logs - @hook_logs ||= hook.web_hook_logs.recent.page(params[:page]).without_count - end - def trigger_values ProjectHook.triggers.values end + + def authorize_destroy_project_hook! + render_404 unless can?(current_user, :destroy_web_hook, hook) + end end diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb index 41daeddcf7f..208fbc40556 100644 --- a/app/controllers/projects/imports_controller.rb +++ b/app/controllers/projects/imports_controller.rb @@ -56,7 +56,7 @@ class Projects::ImportsController < Projects::ApplicationController end def require_namespace_project_creation_permission - render_404 unless can?(current_user, :admin_project, @project) || can?(current_user, :create_projects, @project.namespace) + render_404 unless can?(current_user, :admin_project, @project) || can?(current_user, :import_projects, @project.namespace) end def redirect_if_progress diff --git a/app/controllers/projects/incidents_controller.rb b/app/controllers/projects/incidents_controller.rb index 8e4fbf24ca2..7121096bd77 100644 --- a/app/controllers/projects/incidents_controller.rb +++ b/app/controllers/projects/incidents_controller.rb @@ -10,7 +10,7 @@ class Projects::IncidentsController < Projects::ApplicationController push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items_mvc, @project&.work_items_mvc_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?) - push_frontend_feature_flag(:incident_event_tags, project) + push_frontend_feature_flag(:moved_mr_sidebar, project) end feature_category :incident_management diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 21227d62023..642d5943854 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -20,7 +20,8 @@ class Projects::IssuesController < Projects::ApplicationController before_action :disable_query_limiting, only: [:create_merge_request, :move, :bulk_update] before_action :check_issues_available! before_action :issue, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) } - before_action :redirect_if_work_item, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) } + before_action :redirect_if_work_item, unless: ->(c) { work_item_redirect_except_actions.include?(c.action_name.to_sym) } + before_action :require_incident_for_incident_routes, only: :show after_action :log_issue_show, only: :show @@ -45,8 +46,10 @@ class Projects::IssuesController < Projects::ApplicationController before_action do push_frontend_feature_flag(:preserve_unchanged_markdown, project) - push_frontend_feature_flag(:content_editor_on_issues, project) + push_frontend_feature_flag(:content_editor_on_issues, project&.group) + push_force_frontend_feature_flag(:content_editor_on_issues, project&.content_editor_on_issues_feature_flag_enabled?) push_frontend_feature_flag(:service_desk_new_note_email_native_attachments, project) + push_frontend_feature_flag(:saved_replies, current_user) end before_action only: [:index, :show] do @@ -64,8 +67,7 @@ class Projects::IssuesController < Projects::ApplicationController push_force_frontend_feature_flag(:work_items_mvc, project&.work_items_mvc_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?) push_frontend_feature_flag(:epic_widget_edit_confirmation, project) - push_frontend_feature_flag(:use_iid_in_work_items_path, project&.group) - push_frontend_feature_flag(:incident_event_tags, project) + push_frontend_feature_flag(:moved_mr_sidebar, project) end around_action :allow_gitaly_ref_name_caching, only: [:discussions] @@ -389,6 +391,10 @@ class Projects::IssuesController < Projects::ApplicationController private + def work_item_redirect_except_actions + ISSUES_EXCEPT_ACTIONS + end + def render_by_create_result_error(result) Gitlab::AppLogger.warn( message: 'Cannot create issue', @@ -443,11 +449,16 @@ class Projects::IssuesController < Projects::ApplicationController def redirect_if_work_item return unless use_work_items_path?(issue) - if Feature.enabled?(:use_iid_in_work_items_path, project.group) - redirect_to project_work_items_path(project, issue.iid, params: request.query_parameters.merge(iid_path: true)) - else - redirect_to project_work_items_path(project, issue.id, params: request.query_parameters) - end + redirect_to project_work_items_path(project, issue.iid, params: request.query_parameters) + end + + def require_incident_for_incident_routes + return unless params[:incident_tab].present? + return if issue.work_item_type&.incident? + + # Redirect instead of 404 to gracefully handle + # issue type changes + redirect_to project_issue_path(project, issue) end end diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index 3fea5c694f7..79ddcbf732d 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -8,8 +8,8 @@ class Projects::JobsController < Projects::ApplicationController urgency :low, [:index, :show, :trace, :retry, :play, :cancel, :unschedule, :erase, :raw] - before_action :find_job_as_build, except: [:index, :play, :show, :retry] - before_action :find_job_as_processable, only: [:play, :show, :retry] + before_action :find_job_as_build, except: [:index, :play, :retry] + before_action :find_job_as_processable, only: [:play, :retry] before_action :authorize_read_build_trace!, only: [:trace, :raw] before_action :authorize_read_build! before_action :authorize_update_build!, @@ -128,8 +128,7 @@ class Projects::JobsController < Projects::ApplicationController service_response = Ci::BuildEraseService.new(@build, current_user).execute if service_response.success? - redirect_to project_job_path(project, @build), - notice: _("Job has been successfully erased!") + redirect_to project_job_path(project, @build), notice: _("Job has been successfully erased!") else head service_response.http_status end @@ -138,9 +137,7 @@ class Projects::JobsController < Projects::ApplicationController def raw if @build.trace.archived? workhorse_set_content_type! - send_upload(@build.job_artifacts_trace.file, - send_params: raw_send_params, - redirect_params: raw_redirect_params) + send_upload(@build.job_artifacts_trace.file, send_params: raw_send_params, redirect_params: raw_redirect_params) else @build.trace.read do |stream| if stream.file? @@ -234,10 +231,12 @@ class Projects::JobsController < Projects::ApplicationController end def build_service_specification - @build.service_specification(service: params['service'], - port: params['port'], - path: params['path'], - subprotocols: proxy_subprotocol) + @build.service_specification( + service: params['service'], + port: params['port'], + path: params['path'], + subprotocols: proxy_subprotocol + ) end def proxy_subprotocol diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index 14f2e372bc5..649bead0b6d 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -82,9 +82,7 @@ class Projects::LabelsController < Projects::ApplicationController @label.destroy @labels = find_labels - redirect_to project_labels_path(@project), - status: :found, - notice: 'Label was removed' + redirect_to project_labels_path(@project), status: :found, notice: 'Label was removed' end def remove_priority @@ -138,8 +136,9 @@ class Projects::LabelsController < Projects::ApplicationController respond_to do |format| format.html do - redirect_to(project_labels_path(@project), - notice: _('Failed to promote label due to internal error. Please contact administrators.')) + redirect_to( + project_labels_path(@project), + notice: _('Failed to promote label due to internal error. Please contact administrators.')) end format.js end @@ -165,13 +164,14 @@ class Projects::LabelsController < Projects::ApplicationController end def find_labels - @available_labels ||= - LabelsFinder.new(current_user, - project_id: @project.id, - include_ancestor_groups: true, - search: params[:search], - subscribed: params[:subscribed], - sort: sort).execute + @available_labels ||= LabelsFinder.new( + current_user, + project_id: @project.id, + include_ancestor_groups: true, + search: params[:search], + subscribed: params[:subscribed], + sort: sort + ).execute end def sort diff --git a/app/controllers/projects/merge_requests/application_controller.rb b/app/controllers/projects/merge_requests/application_controller.rb index be44c78ac9d..6d1b1ced4eb 100644 --- a/app/controllers/projects/merge_requests/application_controller.rb +++ b/app/controllers/projects/merge_requests/application_controller.rb @@ -7,6 +7,11 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont feature_category :code_review_workflow + before_action do + push_frontend_feature_flag(:content_editor_on_issues, project&.group) + push_force_frontend_feature_flag(:content_editor_on_issues, project&.content_editor_on_issues_feature_flag_enabled?) + end + private def merge_request diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index 3b399e3294e..3a03831ab88 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -114,11 +114,10 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap @target_project = @merge_request.target_project @source_project = @merge_request.source_project - @commits = - set_commits_for_rendering( - @merge_request.recent_commits.with_latest_pipeline(@merge_request.source_branch), - commits_count: @merge_request.commits_count - ) + @commits = set_commits_for_rendering( + @merge_request.recent_commits.with_latest_pipeline(@merge_request.source_branch), + commits_count: @merge_request.commits_count + ) @commit = @merge_request.diff_head_commit diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index d92ef3de6d9..ad3b79b604c 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -32,18 +32,28 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo before_action :authenticate_user!, only: [:assign_related_issues] before_action :check_user_can_push_to_source_branch!, only: [:rebase] + before_action only: :index do + push_frontend_feature_flag(:mr_approved_filter, type: :ops) + end + before_action only: [:show, :diffs] do + push_frontend_feature_flag(:content_editor_on_issues, project&.group) + push_force_frontend_feature_flag(:content_editor_on_issues, project&.content_editor_on_issues_feature_flag_enabled?) push_frontend_feature_flag(:core_security_mr_widget_counts, project) push_frontend_feature_flag(:issue_assignees_widget, @project) push_frontend_feature_flag(:refactor_security_extension, @project) - push_frontend_feature_flag(:refactor_code_quality_inline_findings, project) + push_frontend_feature_flag(:deprecate_vulnerabilities_feedback, @project) push_frontend_feature_flag(:moved_mr_sidebar, project) + push_frontend_feature_flag(:single_file_file_by_file, project) push_frontend_feature_flag(:mr_experience_survey, project) push_frontend_feature_flag(:realtime_mr_status_change, project) - end - - before_action do - push_frontend_feature_flag(:permit_all_shared_groups_for_approval, @project) + push_frontend_feature_flag(:realtime_approvals, project) + push_frontend_feature_flag(:saved_replies, current_user) + push_frontend_feature_flag(:code_quality_inline_drawer, project) + push_frontend_feature_flag(:hide_create_issue_resolve_all, project) + push_frontend_feature_flag(:auto_merge_labels_mr_widget, project) + push_force_frontend_feature_flag(:summarize_my_code_review, summarize_my_code_review_enabled?) + push_frontend_feature_flag(:mr_activity_filters, current_user) end around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :diffs, :discussions] @@ -123,8 +133,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo recent_commits, commits_count: @merge_request.commits_count ) + commits_count = @merge_request.preparing? ? '-' : @merge_request.commits_count + @merge_request.context_commits_count - render json: { html: view_to_html_string('projects/merge_requests/_commits'), next_page: @next_page } + render json: { html: view_to_html_string('projects/merge_requests/_commits'), next_page: @next_page, count: commits_count } end def pipelines @@ -266,6 +277,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo status = merge! + Gitlab::ApplicationContext.push(merge_action_status: status.to_s) + if @merge_request.merge_error render json: { status: status, merge_error: @merge_request.merge_error } else @@ -383,10 +396,12 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo @merge_request.merge_request_reviewers.map(&:cache_key) ] - render_cached(@merge_request, - with: serializer, - cache_context: ->(_) { [Digest::SHA256.hexdigest(cache_context.to_s)] }, - serializer: params[:serializer]) + render_cached( + @merge_request, + with: serializer, + cache_context: ->(_) { [Digest::SHA256.hexdigest(cache_context.to_s)] }, + serializer: params[:serializer] + ) else render json: serializer.represent(@merge_request, serializer: params[:serializer]) end @@ -485,8 +500,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo AutoMergeService.new(project, current_user, merge_params).update(merge_request) else AutoMergeService.new(project, current_user, merge_params) - .execute(merge_request, - params[:auto_merge_strategy] || AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS) + .execute(merge_request, params[:auto_merge_strategy] || AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS) end else @merge_request.merge_async(current_user.id, merge_params) @@ -591,6 +605,17 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo Date.strptime(date, "%Y-%m-%d")&.to_time&.to_i if date rescue Date::Error, TypeError end + + def summarize_my_code_review_enabled? + namespace = project&.group&.root_ancestor + return false if namespace.nil? + + Feature.enabled?(:summarize_my_code_review, current_user) && + namespace.group_namespace? && + namespace.licensed_feature_available?(:summarize_my_mr_code_review) && + Gitlab::Llm::StageCheck.available?(namespace, :summarize_my_mr_code_review) && + merge_request.send_to_ai? + end end Projects::MergeRequestsController.prepend_mod_with('Projects::MergeRequestsController') diff --git a/app/controllers/projects/metrics/dashboards/builder_controller.rb b/app/controllers/projects/metrics/dashboards/builder_controller.rb index a6b57798923..02e3afcdc80 100644 --- a/app/controllers/projects/metrics/dashboards/builder_controller.rb +++ b/app/controllers/projects/metrics/dashboards/builder_controller.rb @@ -10,6 +10,8 @@ module Projects urgency :low def panel_preview + return not_found if Feature.enabled?(:remove_monitor_metrics) + respond_to do |format| format.json do if rendered_panel.success? diff --git a/app/controllers/projects/metrics_dashboard_controller.rb b/app/controllers/projects/metrics_dashboard_controller.rb index 08757d11912..510c882d537 100644 --- a/app/controllers/projects/metrics_dashboard_controller.rb +++ b/app/controllers/projects/metrics_dashboard_controller.rb @@ -9,6 +9,9 @@ module Projects include Gitlab::Utils::StrongMemoize before_action :authorize_metrics_dashboard! + before_action :render_404, only: :show, if: -> do + Feature.enabled?(:remove_monitor_metrics) + end feature_category :metrics urgency :low diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 78108cf3478..569a514b23b 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -85,6 +85,24 @@ class Projects::MilestonesController < Projects::ApplicationController end end end + rescue ActiveRecord::StaleObjectError + respond_to do |format| + format.html do + @conflict = true + render :edit + end + + format.json do + render json: { + errors: [ + format( + _("Someone edited this %{model_name} at the same time you did. Please refresh your browser and make sure your changes will not unintentionally remove theirs."), + model_name: _('milestone') + ) + ] + }, status: :conflict + end + end end def promote @@ -152,7 +170,15 @@ class Projects::MilestonesController < Projects::ApplicationController end def milestone_params - params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event) + params.require(:milestone) + .permit( + :description, + :due_date, + :lock_version, + :start_date, + :state_event, + :title + ) end def search_params diff --git a/app/controllers/projects/ml/candidates_controller.rb b/app/controllers/projects/ml/candidates_controller.rb index b702edb858e..e534000f494 100644 --- a/app/controllers/projects/ml/candidates_controller.rb +++ b/app/controllers/projects/ml/candidates_controller.rb @@ -3,18 +3,29 @@ module Projects module Ml class CandidatesController < ApplicationController - before_action :check_feature_flag + before_action :check_feature_flag, :set_candidate feature_category :mlops - def show - @candidate = ::Ml::Candidate.with_project_id_and_iid(@project.id, params['iid']) + def show; end - render_404 unless @candidate.present? + def destroy + @experiment = @candidate.experiment + @candidate.destroy! + + redirect_to project_ml_experiment_path(@project, @experiment.iid), + status: :found, + notice: s_("MlExperimentTracking|Candidate removed") end private + def set_candidate + @candidate = ::Ml::Candidate.with_project_id_and_iid(@project.id, params['iid']) + + render_404 unless @candidate.present? + end + def check_feature_flag render_404 unless Feature.enabled?(:ml_experiment_tracking, @project) end diff --git a/app/controllers/projects/ml/experiments_controller.rb b/app/controllers/projects/ml/experiments_controller.rb index 00b965542f6..dece3f98c57 100644 --- a/app/controllers/projects/ml/experiments_controller.rb +++ b/app/controllers/projects/ml/experiments_controller.rb @@ -6,6 +6,7 @@ module Projects include Projects::Ml::ExperimentsHelper before_action :check_feature_flag + before_action :set_experiment, only: [:show, :destroy] feature_category :mlops @@ -22,21 +23,34 @@ module Projects end def show - @experiment = ::Ml::Experiment.by_project_id_and_iid(@project.id, params[:id]) - - return redirect_to project_ml_experiments_path(@project) unless @experiment.present? - find_params = params .transform_keys(&:underscore) .permit(:name, :order_by, :sort, :order_by_type) - paginator = CandidateFinder - .new(@experiment, find_params) - .execute - .keyset_paginate(cursor: params[:cursor], per_page: MAX_CANDIDATES_PER_PAGE) + finder = CandidateFinder.new(@experiment, find_params) - @candidates = paginator.records.each(&:artifact_lazy) - @page_info = page_info(paginator) + respond_to do |format| + format.csv do + csv_data = ::Ml::CandidatesCsvPresenter.new(finder.execute).present + + send_data(csv_data, type: 'text/csv; charset=utf-8', filename: 'candidates.csv') + end + + format.html do + paginator = finder.execute.keyset_paginate(cursor: params[:cursor], per_page: MAX_CANDIDATES_PER_PAGE) + + @candidates = paginator.records + @page_info = page_info(paginator) + end + end + end + + def destroy + @experiment.destroy + + redirect_to project_ml_experiments_path(@project), + status: :found, + notice: s_("MlExperimentTracking|Experiment removed") end private @@ -44,6 +58,12 @@ module Projects def check_feature_flag render_404 unless Feature.enabled?(:ml_experiment_tracking, @project) end + + def set_experiment + @experiment = ::Ml::Experiment.by_project_id_and_iid(@project.id, params[:iid]) + + render_404 unless @experiment + end end end end diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb index db0762a6cff..332d33b8e52 100644 --- a/app/controllers/projects/pages_controller.rb +++ b/app/controllers/projects/pages_controller.rb @@ -43,9 +43,7 @@ class Projects::PagesController < Projects::ApplicationController respond_to do |format| format.html do - redirect_to project_pages_path(@project), - status: :found, - notice: 'Pages were scheduled for removal' + redirect_to project_pages_path(@project), status: :found, notice: 'Pages were scheduled for removal' end end end @@ -77,7 +75,15 @@ class Projects::PagesController < Projects::ApplicationController end def project_params_attributes - %i[pages_https_only] + attributes = %i[pages_https_only] + + return attributes unless Feature.enabled?(:pages_unique_domain, @project) + + attributes + [ + project_setting_attributes: [ + :pages_unique_domain_enabled + ] + ] end end diff --git a/app/controllers/projects/pages_domains_controller.rb b/app/controllers/projects/pages_domains_controller.rb index 43952a2efe4..5cb69e8bf99 100644 --- a/app/controllers/projects/pages_domains_controller.rb +++ b/app/controllers/projects/pages_domains_controller.rb @@ -69,9 +69,7 @@ class Projects::PagesDomainsController < Projects::ApplicationController respond_to do |format| format.html do - redirect_to project_pages_path(@project), - status: :found, - notice: 'Domain was removed' + redirect_to project_pages_path(@project), status: :found, notice: 'Domain was removed' end format.js end diff --git a/app/controllers/projects/performance_monitoring/dashboards_controller.rb b/app/controllers/projects/performance_monitoring/dashboards_controller.rb index d043f8d0b9f..1255ec1dde2 100644 --- a/app/controllers/projects/performance_monitoring/dashboards_controller.rb +++ b/app/controllers/projects/performance_monitoring/dashboards_controller.rb @@ -16,6 +16,8 @@ module Projects urgency :low def create + return not_found if Feature.enabled?(:remove_monitor_metrics) + result = ::Metrics::Dashboard::CloneDashboardService.new(project, current_user, dashboard_params).execute if result[:status] == :success @@ -26,6 +28,8 @@ module Projects end def update + return not_found if Feature.enabled?(:remove_monitor_metrics) + result = ::Metrics::Dashboard::UpdateDashboardService.new(project, current_user, dashboard_params.merge(file_content_params)).execute if result[:status] == :success diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb index 19d031bd59b..fb332fec3b5 100644 --- a/app/controllers/projects/pipeline_schedules_controller.rb +++ b/app/controllers/projects/pipeline_schedules_controller.rb @@ -8,8 +8,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController before_action :authorize_read_pipeline_schedule! before_action :authorize_create_pipeline_schedule!, only: [:new, :create] before_action :authorize_update_pipeline_schedule!, only: [:edit, :update] - before_action :authorize_take_ownership_pipeline_schedule!, only: [:take_ownership] - before_action :authorize_admin_pipeline_schedule!, only: [:destroy] + before_action :authorize_admin_pipeline_schedule!, only: [:take_ownership, :destroy] before_action :push_schedule_feature_flag, only: [:index, :new, :edit] feature_category :continuous_integration @@ -78,9 +77,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController if schedule.destroy redirect_to pipeline_schedules_path(@project), status: :found else - redirect_to pipeline_schedules_path(@project), - status: :forbidden, - alert: _("Failed to remove the pipeline schedule") + redirect_to pipeline_schedules_path(@project), status: :forbidden, alert: _("Failed to remove the pipeline schedule") end end @@ -113,10 +110,6 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController return access_denied! unless can?(current_user, :update_pipeline_schedule, schedule) end - def authorize_take_ownership_pipeline_schedule! - return access_denied! unless can?(current_user, :take_ownership_pipeline_schedule, schedule) - end - def authorize_admin_pipeline_schedule! return access_denied! unless can?(current_user, :admin_pipeline_schedule, schedule) end diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 10f58a9f479..39ebcd60e9a 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -2,23 +2,23 @@ class Projects::PipelinesController < Projects::ApplicationController include ::Gitlab::Utils::StrongMemoize - include RedisTracking + include ProductAnalyticsTracking include ProductAnalyticsTracking include ProjectStatsRefreshConflictsGuard urgency :low, [ :index, :new, :builds, :show, :failures, :create, :stage, :retry, :dag, :cancel, :test_report, - :charts, :config_variables, :destroy, :status + :charts, :destroy, :status ] before_action :disable_query_limiting, only: [:create, :retry] - before_action :pipeline, except: [:index, :new, :create, :charts, :config_variables] + before_action :pipeline, except: [:index, :new, :create, :charts] before_action :set_pipeline_path, only: [:show] before_action :authorize_read_pipeline! before_action :authorize_read_build!, only: [:index, :show] before_action :authorize_read_ci_cd_analytics!, only: [:charts] - before_action :authorize_create_pipeline!, only: [:new, :create, :config_variables] + before_action :authorize_create_pipeline!, only: [:new, :create] before_action :authorize_update_pipeline!, only: [:retry, :cancel] before_action :ensure_pipeline, only: [:show, :downloadable_artifacts] before_action :reject_if_build_artifacts_size_refreshing!, only: [:destroy] @@ -28,24 +28,24 @@ class Projects::PipelinesController < Projects::ApplicationController around_action :allow_gitaly_ref_name_caching, only: [:index, :show] - track_custom_event :charts, + track_event :charts, name: 'p_analytics_pipelines', action: 'perform_analytics_usage_action', label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', destinations: %i[redis_hll snowplow] - track_redis_hll_event :charts, name: 'p_analytics_ci_cd_pipelines', if: -> { should_track_ci_cd_pipelines? } - track_redis_hll_event :charts, name: 'p_analytics_ci_cd_deployment_frequency', if: -> { should_track_ci_cd_deployment_frequency? } - track_redis_hll_event :charts, name: 'p_analytics_ci_cd_lead_time', if: -> { should_track_ci_cd_lead_time? } - track_redis_hll_event :charts, name: 'p_analytics_ci_cd_time_to_restore_service', if: -> { should_track_ci_cd_time_to_restore_service? } - track_redis_hll_event :charts, name: 'p_analytics_ci_cd_change_failure_rate', if: -> { should_track_ci_cd_change_failure_rate? } + track_event :charts, name: 'p_analytics_ci_cd_pipelines', conditions: -> { should_track_ci_cd_pipelines? } + track_event :charts, name: 'p_analytics_ci_cd_deployment_frequency', conditions: -> { should_track_ci_cd_deployment_frequency? } + track_event :charts, name: 'p_analytics_ci_cd_lead_time', conditions: -> { should_track_ci_cd_lead_time? } + track_event :charts, name: 'p_analytics_ci_cd_time_to_restore_service', conditions: -> { should_track_ci_cd_time_to_restore_service? } + track_event :charts, name: 'p_analytics_ci_cd_change_failure_rate', conditions: -> { should_track_ci_cd_change_failure_rate? } wrap_parameters Ci::Pipeline POLLING_INTERVAL = 10_000 feature_category :continuous_integration, [ - :charts, :show, :config_variables, :stage, :cancel, :retry, + :charts, :show, :stage, :cancel, :retry, :builds, :dag, :failures, :status, :index, :create, :new, :destroy ] @@ -61,9 +61,7 @@ class Projects::PipelinesController < Projects::ApplicationController @pipelines_count = limited_pipelines_count(project) respond_to do |format| - format.html do - enable_runners_availability_section_experiment - end + format.html format.json do Gitlab::PollingInterval.set_header(response, interval: POLLING_INTERVAL) @@ -98,15 +96,15 @@ class Projects::PipelinesController < Projects::ApplicationController end format.json do if service_response.success? - render json: PipelineSerializer - .new(project: project, current_user: current_user) - .represent(@pipeline), - status: :created + render json: PipelineSerializer.new(project: project, current_user: current_user).represent(@pipeline), + status: :created else - render json: { errors: @pipeline.error_messages.map(&:content), - warnings: @pipeline.warning_messages(limit: ::Gitlab::Ci::Warnings::MAX_LIMIT).map(&:content), - total_warnings: @pipeline.warning_messages.length }, - status: :bad_request + bad_request_json = { + errors: @pipeline.error_messages.map(&:content), + warnings: @pipeline.warning_messages(limit: ::Gitlab::Ci::Warnings::MAX_LIMIT).map(&:content), + total_warnings: @pipeline.warning_messages.length + } + render json: bad_request_json, status: :bad_request end end end @@ -216,18 +214,6 @@ class Projects::PipelinesController < Projects::ApplicationController end end - def config_variables - respond_to do |format| - format.json do - # Even if the parameter name is `sha`, it is actually a ref name. We always send `ref` to the endpoint. - # See: https://gitlab.com/gitlab-org/gitlab/-/issues/389065 - result = Ci::ListConfigVariablesService.new(@project, current_user).execute(params[:sha]) - - result.nil? ? head(:no_content) : render(json: result) - end - end - end - def downloadable_artifacts render json: Ci::DownloadableArtifactSerializer.new( project: project, @@ -241,7 +227,12 @@ class Projects::PipelinesController < Projects::ApplicationController PipelineSerializer .new(project: @project, current_user: @current_user) .with_pagination(request, response) - .represent(@pipelines, disable_coverage: true, preload: true) + .represent( + @pipelines, + disable_coverage: true, + preload: true, + disable_manual_and_scheduled_actions: true + ) end def render_show @@ -274,29 +265,34 @@ class Projects::PipelinesController < Projects::ApplicationController # rubocop: disable CodeReuse/ActiveRecord def pipeline - @pipeline ||= if params[:id].blank? && params[:latest] - latest_pipeline - else - project - .all_pipelines - .includes(builds: :tags, user: :status) - .find(params[:id]) - .present(current_user: current_user) - end + return @pipeline if defined?(@pipeline) + + pipelines = + if find_latest_pipeline? + project.latest_pipelines(params['ref']) + else + project.all_pipelines.id_in(params[:id]) + end + + @pipeline = pipelines + .includes(builds: :tags, user: :status) + .take + &.present(current_user: current_user) + + @pipeline || not_found end # rubocop: enable CodeReuse/ActiveRecord def set_pipeline_path - @pipeline_path ||= if params[:id].blank? && params[:latest] + @pipeline_path ||= if find_latest_pipeline? latest_project_pipelines_path(@project, params['ref']) else project_pipeline_path(@project, @pipeline) end end - def latest_pipeline - @project.latest_pipeline(params['ref']) - &.present(current_user: current_user) + def find_latest_pipeline? + params[:id].blank? && params[:latest] end def disable_query_limiting @@ -326,17 +322,6 @@ class Projects::PipelinesController < Projects::ApplicationController params.permit(:scope, :username, :ref, :status, :source) end - def enable_runners_availability_section_experiment - return unless current_user - return unless can?(current_user, :create_pipeline, project) - return if @pipelines_count.to_i > 0 - return if helpers.has_gitlab_ci?(project) - - experiment(:runners_availability_section, namespace: project.root_ancestor) do |e| - e.candidate {} - end - end - def should_track_ci_cd_pipelines? params[:chart].blank? || params[:chart] == 'pipelines' end diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 543ffa637e1..f4b96177b0f 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -47,7 +47,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController end def membershipable_members - query_members_via_project_namespace_enabled? ? project.namespace_members : project.members + project.namespace_members end def plain_source_type @@ -67,15 +67,11 @@ class Projects::ProjectMembersController < Projects::ApplicationController end def members_and_requesters - query_members_via_project_namespace_enabled? ? project.namespace_members_and_requesters : super + project.namespace_members_and_requesters end def requesters - query_members_via_project_namespace_enabled? ? project.namespace_requesters : super - end - - def query_members_via_project_namespace_enabled? - Feature.enabled?(:project_members_index_by_project_namespace, project) + project.namespace_requesters end end diff --git a/app/controllers/projects/prometheus/metrics_controller.rb b/app/controllers/projects/prometheus/metrics_controller.rb index db5471ea322..c20c80ba334 100644 --- a/app/controllers/projects/prometheus/metrics_controller.rb +++ b/app/controllers/projects/prometheus/metrics_controller.rb @@ -68,7 +68,7 @@ module Projects if @metric.persisted? redirect_to edit_project_settings_integration_path(project, ::Integrations::Prometheus), - notice: _('Metric was successfully added.') + notice: _('Metric was successfully added.') else render 'new' end @@ -79,7 +79,7 @@ module Projects if @metric.update(metrics_params) redirect_to edit_project_settings_integration_path(project, ::Integrations::Prometheus), - notice: _('Metric was successfully updated.') + notice: _('Metric was successfully updated.') else render 'edit' end diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index 895a9a00624..79b5990abba 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -6,8 +6,6 @@ class Projects::RawController < Projects::ApplicationController include SendsBlob include StaticObjectExternalStorage - skip_before_action :default_cache_headers, only: :show - prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:blob) } before_action :assign_ref_vars diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index f55fc2242a4..4c2bd2a9d42 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -22,7 +22,7 @@ class Projects::RefsController < Projects::ApplicationController when "tree" project_tree_path(@project, @id) when "blob" - project_blob_path(@project, @id, ref_type: ref_type) + project_blob_path(@project, @id) when "graph" project_network_path(@project, @id, ref_type: ref_type) when "graphs" diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb index 1cd4c5b6137..4a9282432fd 100644 --- a/app/controllers/projects/repositories_controller.rb +++ b/app/controllers/projects/repositories_controller.rb @@ -8,8 +8,6 @@ class Projects::RepositoriesController < Projects::ApplicationController prepend_before_action(only: [:archive]) { authenticate_sessionless_user!(:archive) } - skip_before_action :default_cache_headers, only: :archive - # Authorize before_action :check_archive_rate_limiting!, only: :archive before_action :require_non_empty_project, except: :create @@ -29,9 +27,8 @@ class Projects::RepositoriesController < Projects::ApplicationController end def archive - return render_404 if html_request? - set_cache_headers + return if archive_not_modified? send_git_archive @repository, **repo_params @@ -49,9 +46,14 @@ class Projects::RepositoriesController < Projects::ApplicationController def set_cache_headers commit_id = archive_metadata['CommitId'] - expires_in(cache_max_age(commit_id), - public: Guest.can?(:download_code, project), must_revalidate: true, stale_if_error: 5.minutes, - stale_while_revalidate: 1.minute, 's-maxage': 1.minute) + expires_in( + cache_max_age(commit_id), + public: Guest.can?(:download_code, project), + must_revalidate: true, + stale_if_error: 5.minutes, + stale_while_revalidate: 1.minute, + 's-maxage': 1.minute + ) fresh_when(strong_etag: [commit_id, archive_metadata['ArchivePath']]) end diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb index 6f896244acb..a7df1a0a412 100644 --- a/app/controllers/projects/runner_projects_controller.rb +++ b/app/controllers/projects/runner_projects_controller.rb @@ -16,7 +16,8 @@ class Projects::RunnerProjectsController < Projects::ApplicationController path = project_runners_path(project) if ::Ci::Runners::AssignRunnerService.new(@runner, @project, current_user).execute.success? - redirect_to path, notice: s_('Runners|Runner assigned to project.') + flash[:success] = s_('Runners|Runner assigned to project.') + redirect_to path else assign_to_messages = @runner.errors.messages[:assign_to] alert = assign_to_messages&.join(',') || 'Failed adding runner to project' @@ -30,6 +31,7 @@ class Projects::RunnerProjectsController < Projects::ApplicationController ::Ci::Runners::UnassignRunnerService.new(runner_project, current_user).execute - redirect_to project_runners_path(project), status: :found, notice: s_('Runners|Runner unassigned from project.') + flash[:success] = s_('Runners|Runner unassigned from project.') + redirect_to project_runners_path(project), status: :found end end diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index ee12b85b3a4..2b2c2cef8e2 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -2,7 +2,8 @@ class Projects::RunnersController < Projects::ApplicationController before_action :authorize_admin_build! - before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show] + before_action :authorize_create_runner!, only: [:new, :register] + before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show, :register] feature_category :runner urgency :low @@ -22,6 +23,14 @@ class Projects::RunnersController < Projects::ApplicationController end end + def new + render_404 unless create_runner_workflow_for_namespace_enabled? + end + + def register + render_404 unless create_runner_workflow_for_namespace_enabled? && runner.registration_available? + end + def destroy if @runner.only_for?(project) Ci::Runners::UnregisterRunnerService.new(@runner, current_user).execute @@ -75,4 +84,8 @@ class Projects::RunnersController < Projects::ApplicationController def runner_params params.require(:runner).permit(Ci::Runner::FORM_EDITABLE) end + + def create_runner_workflow_for_namespace_enabled? + Feature.enabled?(:create_runner_workflow_for_namespace, project.namespace) + end end diff --git a/app/controllers/projects/security/configuration_controller.rb b/app/controllers/projects/security/configuration_controller.rb index 00a2a5d1193..ee2e60b5a1a 100644 --- a/app/controllers/projects/security/configuration_controller.rb +++ b/app/controllers/projects/security/configuration_controller.rb @@ -32,9 +32,7 @@ module Projects end def configuration_presenter - ::Projects::Security::ConfigurationPresenter.new(project, - **presenter_attributes, - current_user: current_user) + ::Projects::Security::ConfigurationPresenter.new(project, **presenter_attributes, current_user: current_user) end def presenter_attributes diff --git a/app/controllers/projects/settings/access_tokens_controller.rb b/app/controllers/projects/settings/access_tokens_controller.rb index 0884816ef62..af1527ba6a3 100644 --- a/app/controllers/projects/settings/access_tokens_controller.rb +++ b/app/controllers/projects/settings/access_tokens_controller.rb @@ -7,7 +7,7 @@ module Projects include AccessTokensActions layout 'project_settings' - feature_category :authentication_and_authorization + feature_category :system_access alias_method :resource, :project diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index 4ca665679c0..ce760051f79 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -13,7 +13,11 @@ module Projects before_action :define_variables before_action do - push_frontend_feature_flag(:ci_inbound_job_token_scope, @project) + push_frontend_feature_flag(:ci_variables_pages, current_user) + push_frontend_feature_flag(:ci_limit_environment_scope, @project) + push_frontend_feature_flag(:create_runner_workflow_for_namespace, @project.namespace) + push_frontend_feature_flag(:frozen_outbound_job_token_scopes, @project) + push_frontend_feature_flag(:frozen_outbound_job_token_scopes_override, @project) end helper_method :highlight_badge @@ -131,7 +135,7 @@ module Projects @shared_runners_count = active_shared_runners.count @shared_runners = active_shared_runners.page(params[:shared_runners_page]).per(NUMBER_OF_RUNNERS_PER_PAGE).with_tags - parent_group_runners = ::Ci::Runner.belonging_to_parent_group_of_project(@project.id) + parent_group_runners = ::Ci::Runner.belonging_to_parent_groups_of_project(@project.id) @group_runners_count = parent_group_runners.count @group_runners = parent_group_runners.page(params[:group_runners_page]).per(NUMBER_OF_RUNNERS_PER_PAGE).with_tags end diff --git a/app/controllers/projects/settings/integrations_controller.rb b/app/controllers/projects/settings/integrations_controller.rb index 16c1373df2b..a0e72fb1687 100644 --- a/app/controllers/projects/settings/integrations_controller.rb +++ b/app/controllers/projects/settings/integrations_controller.rb @@ -25,6 +25,7 @@ module Projects end def edit + render_404 if integration.to_param == 'prometheus' && Feature.enabled?(:remove_monitor_metrics) end def update diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb index 478d9f0b38e..952c9e90a2c 100644 --- a/app/controllers/projects/settings/operations_controller.rb +++ b/app/controllers/projects/settings/operations_controller.rb @@ -133,7 +133,11 @@ module Projects ], grafana_integration_attributes: [:token, :grafana_url, :enabled] - } + }.tap do |potential_params| + if Feature.enabled?(:remove_monitor_metrics) + potential_params.except!(:metrics_setting_attributes, :grafana_integration_attributes) + end + end end end end diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb index 74d730db026..a6f4e2fcd73 100644 --- a/app/controllers/projects/settings/repository_controller.rb +++ b/app/controllers/projects/settings/repository_controller.rb @@ -122,21 +122,6 @@ module Projects ] end - def access_levels_options - { - create_access_levels: levels_for_dropdown, - push_access_levels: levels_for_dropdown, - merge_access_levels: levels_for_dropdown - } - end - - def levels_for_dropdown - roles = ProtectedRefAccess::HUMAN_ACCESS_LEVELS.map do |id, text| - { id: id, text: text, before_divider: true } - end - { roles: roles } - end - def protectable_tags_for_dropdown { open_tags: ProtectableDropdown.new(@project, :tags).hash } end @@ -154,7 +139,7 @@ module Projects def load_gon_index gon.push(protectable_tags_for_dropdown) gon.push(protectable_branches_for_dropdown) - gon.push(access_levels_options) + gon.push(helpers.protected_access_levels_for_dropdowns) gon.push(current_project_id: project.id) if project end end diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index 1f7912e15df..495241df912 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -18,7 +18,8 @@ class Projects::TreeController < Projects::ApplicationController before_action do push_frontend_feature_flag(:highlight_js, @project) - push_frontend_feature_flag(:file_line_blame, @project) + push_frontend_feature_flag(:synchronize_fork, @project.fork_source) + push_frontend_feature_flag(:explain_code_chat, current_user) push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) end @@ -49,9 +50,12 @@ class Projects::TreeController < Projects::ApplicationController def create_dir return render_404 unless @commit_params.values.all? - create_commit(Files::CreateDirService, success_notice: _("The directory has been successfully created."), - success_path: project_tree_path(@project, File.join(@branch_name, @dir_name)), - failure_path: project_tree_path(@project, @ref)) + create_commit( + Files::CreateDirService, + success_notice: _("The directory has been successfully created."), + success_path: project_tree_path(@project, File.join(@branch_name, @dir_name)), + failure_path: project_tree_path(@project, @ref) + ) end private diff --git a/app/controllers/projects/usage_quotas_controller.rb b/app/controllers/projects/usage_quotas_controller.rb index d3757eaf481..7037cf8811a 100644 --- a/app/controllers/projects/usage_quotas_controller.rb +++ b/app/controllers/projects/usage_quotas_controller.rb @@ -1,14 +1,18 @@ # frozen_string_literal: true -class Projects::UsageQuotasController < Projects::ApplicationController - before_action :authorize_read_usage_quotas! +module Projects + class UsageQuotasController < Projects::ApplicationController + before_action :authorize_read_usage_quotas! - layout "project_settings" + layout "project_settings" - feature_category :subscription_cost_management - urgency :low + feature_category :consumables_cost_management + urgency :low - def index - @hide_search_settings = true + def index + @hide_search_settings = true + end end end + +Projects::UsageQuotasController.prepend_mod diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb index a83ccccbeae..f7542d68642 100644 --- a/app/controllers/projects/variables_controller.rb +++ b/app/controllers/projects/variables_controller.rb @@ -3,7 +3,7 @@ class Projects::VariablesController < Projects::ApplicationController before_action :authorize_admin_build! - feature_category :pipeline_authoring + feature_category :secrets_management urgency :low, [:show, :update] diff --git a/app/controllers/projects/web_ide_terminals_controller.rb b/app/controllers/projects/web_ide_terminals_controller.rb index cfccc949244..be7423e3919 100644 --- a/app/controllers/projects/web_ide_terminals_controller.rb +++ b/app/controllers/projects/web_ide_terminals_controller.rb @@ -29,10 +29,7 @@ class Projects::WebIdeTerminalsController < Projects::ApplicationController end def create - result = ::Ci::CreateWebIdeTerminalService.new(project, - current_user, - ref: params[:branch]) - .execute + result = ::Ci::CreateWebIdeTerminalService.new(project, current_user, ref: params[:branch]).execute if result[:status] == :error render status: :bad_request, json: result[:message] diff --git a/app/controllers/projects/work_items_controller.rb b/app/controllers/projects/work_items_controller.rb index db9dca14aab..7da31c199a1 100644 --- a/app/controllers/projects/work_items_controller.rb +++ b/app/controllers/projects/work_items_controller.rb @@ -1,15 +1,71 @@ # frozen_string_literal: true class Projects::WorkItemsController < Projects::ApplicationController + include WorkhorseAuthorization + extend Gitlab::Utils::Override + + EXTENSION_ALLOWLIST = %w[csv].map(&:downcase).freeze + + before_action :authorize_import_access!, only: [:import_csv, :authorize] # rubocop:disable Rails/LexicallyScopedActionFilter before_action do push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items_mvc, project&.work_items_mvc_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?) - push_frontend_feature_flag(:use_iid_in_work_items_path, project&.group) + push_force_frontend_feature_flag(:saved_replies, current_user) end feature_category :team_planning + urgency :high, [:authorize] urgency :low + + def import_csv + file = import_params[:file] + return render json: { errors: invalid_file_message }, status: :bad_request unless file_is_valid?(file) + + result = WorkItems::PrepareImportCsvService.new(project, current_user, file: file).execute + + if result.status == :error + render json: { errors: result.message }, status: :bad_request + else + render json: { message: result.message }, status: :ok + end + end + + private + + def import_params + params.permit(:file) + end + + def authorize_import_access! + can_import = can?(current_user, :import_work_items, project) + import_csv_feature_available = Feature.enabled?(:import_export_work_items_csv, project) + return if can_import && import_csv_feature_available + + if current_user || action_name == 'authorize' + render_404 + else + authenticate_user! + end + end + + def invalid_file_message + supported_file_extensions = ".#{EXTENSION_ALLOWLIST.join(', .')}" + format(_("The uploaded file was invalid. Supported file extensions are %{extensions}."), + { extensions: supported_file_extensions }) + end + + def uploader_class + FileUploader + end + + def maximum_size + Gitlab::CurrentSettings.max_attachment_size.megabytes + end + + def file_extension_allowlist + EXTENSION_ALLOWLIST + end end Projects::WorkItemsController.prepend_mod diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index d71b782c62b..0f3143606ff 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -38,8 +38,9 @@ class ProjectsController < Projects::ApplicationController before_action do push_frontend_feature_flag(:highlight_js, @project) - push_frontend_feature_flag(:file_line_blame, @project) - push_frontend_feature_flag(:increase_page_size_exponentially, @project) + push_frontend_feature_flag(:synchronize_fork, @project&.fork_source) + push_frontend_feature_flag(:remove_monitor_metrics, @project) + push_frontend_feature_flag(:explain_code_chat, current_user) push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks) push_licensed_feature(:security_orchestration_policies) if @project.present? && @project.licensed_feature_available?(:security_orchestration_policies) push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?) @@ -78,11 +79,11 @@ class ProjectsController < Projects::ApplicationController @namespace = Namespace.find_by(id: params[:namespace_id]) if params[:namespace_id] return access_denied! if @namespace && !can?(current_user, :create_projects, @namespace) + @parent_group = Group.find_by(id: params[:namespace_id]) + @current_user_group = if current_user.manageable_groups(include_groups_with_developer_maintainer_access: true).count == 1 current_user.manageable_groups(include_groups_with_developer_maintainer_access: true).first - else - nil end @project = Project.new(namespace_id: @namespace&.id) @@ -207,7 +208,7 @@ class ProjectsController < Projects::ApplicationController end def new_issuable_address - return render_404 unless Gitlab::IncomingEmail.supports_issue_creation? + return render_404 unless Gitlab::Email::IncomingEmail.supports_issue_creation? current_user.reset_incoming_email_token! render json: { new_address: @project.new_issuable_address(current_user, params[:issuable_type]) } diff --git a/app/controllers/registrations/welcome_controller.rb b/app/controllers/registrations/welcome_controller.rb index cfb4e939b35..ac8959e0f52 100644 --- a/app/controllers/registrations/welcome_controller.rb +++ b/app/controllers/registrations/welcome_controller.rb @@ -4,13 +4,14 @@ module Registrations class WelcomeController < ApplicationController include OneTrustCSP include GoogleAnalyticsCSP - include RegistrationsTracking layout 'minimal' skip_before_action :authenticate_user!, :required_signup_info, :check_two_factor_requirement, only: [:show, :update] before_action :require_current_user - feature_category :authentication_and_authorization + helper_method :welcome_update_params + + feature_category :user_management def show return redirect_to path_for_signed_in_user(current_user) if completed_welcome_step? @@ -48,16 +49,7 @@ module Registrations params.require(:user).permit(:role, :setup_for_company) end - def requires_confirmation?(user) - return false if user.confirmed? - return false if Feature.enabled?(:soft_email_confirmation) - - true - end - def path_for_signed_in_user(user) - return users_almost_there_path(email: user.email) if requires_confirmation?(user) - stored_location_for(user) || members_activity_path(user.members) end @@ -98,6 +90,11 @@ module Registrations # overridden in EE def track_event(action); end + + # overridden in EE + def welcome_update_params + {} + end end end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index ed0e019d02b..3e6683fc867 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -9,7 +9,6 @@ class RegistrationsController < Devise::RegistrationsController include BizibleCSP include GoogleAnalyticsCSP include PreferredLanguageSwitcher - include RegistrationsTracking include Gitlab::Tracking::Helpers::WeakPasswordErrorEvent layout 'devise' @@ -25,10 +24,12 @@ class RegistrationsController < Devise::RegistrationsController before_action only: [:new] do push_frontend_feature_flag(:gitlab_gtm_datalayer, type: :ops) - push_frontend_feature_flag(:trial_email_validation, type: :development) end - feature_category :authentication_and_authorization + feature_category :user_management + + helper_method :arkose_labs_enabled? + helper_method :registration_path_params def new @resource = build_resource @@ -39,6 +40,7 @@ class RegistrationsController < Devise::RegistrationsController set_resource_fields super do |new_user| + record_arkose_data accept_pending_invitations if new_user.persisted? persist_accepted_terms_if_required(new_user) @@ -51,8 +53,6 @@ class RegistrationsController < Devise::RegistrationsController end after_request_hook(new_user) - - yield new_user if block_given? end # Devise sets a flash message on both successful & failed signups, @@ -128,13 +128,18 @@ class RegistrationsController < Devise::RegistrationsController def after_inactive_sign_up_path_for(resource) Gitlab::AppLogger.info(user_created_message) return new_user_session_path(anchor: 'login-pane') if resource.blocked_pending_approval? - return dashboard_projects_path if Feature.enabled?(:soft_email_confirmation) + return dashboard_projects_path if Gitlab::CurrentSettings.email_confirmation_setting_soft? - # when email confirmation is enabled, path to redirect is saved + # when email_confirmation_setting is set to `hard`, path to redirect is saved # after user confirms and comes back, he will be redirected store_location_for(:redirect, after_sign_up_path) - return identity_verification_redirect_path if custom_confirmation_enabled? + if identity_verification_enabled? + session[:verification_user_id] = resource.id # This is needed to find the user on the identity verification page + User.sticking.stick_or_unstick_request(request.env, :user, resource.id) + + return identity_verification_redirect_path + end Gitlab::Tracking.event(self.class.name, 'render', user: resource) users_almost_there_path(email: resource.email) @@ -143,7 +148,12 @@ class RegistrationsController < Devise::RegistrationsController private def after_sign_up_path - users_sign_up_welcome_path(glm_tracking_params) + users_sign_up_welcome_path + end + + # overridden in EE + def registration_path_params + {} end def track_creation(user:) @@ -183,6 +193,7 @@ class RegistrationsController < Devise::RegistrationsController flash[:alert] = _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') flash.delete :recaptcha_error add_gon_variables + set_minimum_password_length render action: 'new' end @@ -221,7 +232,7 @@ class RegistrationsController < Devise::RegistrationsController def resource @resource ||= Users::RegistrationsBuildService - .new(current_user, sign_up_params.merge({ skip_confirmation: registered_with_invite_email?, + .new(current_user, sign_up_params.merge({ skip_confirmation: skip_confirmation?, preferred_language: preferred_language })) .execute end @@ -230,6 +241,10 @@ class RegistrationsController < Devise::RegistrationsController @devise_mapping ||= Devise.mappings[:user] end + def skip_confirmation? + registered_with_invite_email? + end + def registered_with_invite_email? invite_email = session.delete(:invite_email) @@ -282,17 +297,26 @@ class RegistrationsController < Devise::RegistrationsController current_user end - def identity_verification_redirect_path + def record_arkose_data # overridden by EE module end - def custom_confirmation_enabled? + def identity_verification_enabled? + # overridden by EE module + false + end + + def identity_verification_redirect_path # overridden by EE module end def send_custom_confirmation_instructions # overridden by EE module end + + def arkose_labs_enabled? + false + end end RegistrationsController.prepend_mod_with('RegistrationsController') diff --git a/app/controllers/repositories/git_http_controller.rb b/app/controllers/repositories/git_http_controller.rb index bd3461d8331..4f228ced542 100644 --- a/app/controllers/repositories/git_http_controller.rb +++ b/app/controllers/repositories/git_http_controller.rb @@ -8,6 +8,7 @@ module Repositories prepend_before_action :deny_head_requests, only: [:info_refs] rescue_from Gitlab::GitAccess::ForbiddenError, with: :render_403_with_exception + rescue_from JWT::DecodeError, with: :render_403_with_exception rescue_from Gitlab::GitAccess::NotFoundError, with: :render_404_with_exception rescue_from Gitlab::GitAccessProject::CreationError, with: :render_422_with_exception rescue_from Gitlab::GitAccess::TimeoutError, with: :render_503_with_exception @@ -19,6 +20,7 @@ module Repositories # GET /foo/bar.git/info/refs?service=git-receive-pack (git push) def info_refs log_user_activity if upload_pack? + log_user_activity if receive_pack? && Feature.enabled?(:log_user_git_push_activity) render_ok end @@ -49,6 +51,10 @@ module Repositories git_command == 'git-upload-pack' end + def receive_pack? + git_command == 'git-receive-pack' + end + def git_command if action_name == 'info_refs' params[:service] diff --git a/app/controllers/repositories/lfs_api_controller.rb b/app/controllers/repositories/lfs_api_controller.rb index 83973d07a17..d52ae723eee 100644 --- a/app/controllers/repositories/lfs_api_controller.rb +++ b/app/controllers/repositories/lfs_api_controller.rb @@ -172,13 +172,15 @@ module Repositories LfsObjectsProject.link_to_project!(lfs_object, project) - Gitlab::AppJsonLogger.info(message: "LFS object auto-linked to forked project", - lfs_object_oid: lfs_object.oid, - lfs_object_size: lfs_object.size, - source_project_id: project.fork_source.id, - source_project_path: project.fork_source.full_path, - target_project_id: project.project_id, - target_project_path: project.full_path) + Gitlab::AppJsonLogger.info( + message: "LFS object auto-linked to forked project", + lfs_object_oid: lfs_object.oid, + lfs_object_size: lfs_object.size, + source_project_id: project.fork_source.id, + source_project_path: project.fork_source.full_path, + target_project_id: project.project_id, + target_project_path: project.full_path + ) end end end diff --git a/app/controllers/repositories/lfs_locks_api_controller.rb b/app/controllers/repositories/lfs_locks_api_controller.rb index ea858d63236..52ae9068c75 100644 --- a/app/controllers/repositories/lfs_locks_api_controller.rb +++ b/app/controllers/repositories/lfs_locks_api_controller.rb @@ -37,9 +37,7 @@ module Repositories private def render_json(data, process = true) - render json: build_payload(data, process), - content_type: LfsRequest::CONTENT_TYPE, - status: @result[:http_status] + render json: build_payload(data, process), content_type: LfsRequest::CONTENT_TYPE, status: @result[:http_status] end def build_payload(data, process) diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 1ca34dee3d6..a3c6499bc54 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -3,18 +3,18 @@ class SearchController < ApplicationController include ControllerWithCrossProjectAccessCheck include SearchHelper - include RedisTracking + include ProductAnalyticsTracking include ProductAnalyticsTracking include SearchRateLimitable RESCUE_FROM_TIMEOUT_ACTIONS = [:count, :show, :autocomplete, :aggregations].freeze CODE_SEARCH_LITERALS = %w[blob: extension: path: filename:].freeze - track_custom_event :show, - name: 'i_search_total', - label: 'redis_hll_counters.search.search_total_unique_counts_monthly', - action: 'executed', - destinations: [:redis_hll, :snowplow] + track_event :show, + name: 'i_search_total', + label: 'redis_hll_counters.search.search_total_unique_counts_monthly', + action: 'executed', + destinations: [:redis_hll, :snowplow] def self.search_rate_limited_endpoints %i[show count autocomplete] @@ -24,7 +24,6 @@ class SearchController < ApplicationController before_action :block_anonymous_global_searches, :check_scope_global_search_enabled, except: :opensearch skip_before_action :authenticate_user! - skip_before_action :default_cache_headers, only: :count requires_cross_project_access if: -> do search_term_present = params[:search].present? || params[:term].present? @@ -33,9 +32,6 @@ class SearchController < ApplicationController before_action :check_search_rate_limit!, only: search_rate_limited_endpoints before_action only: :show do - push_frontend_feature_flag(:search_blobs_language_aggregation, current_user) - end - before_action only: :show do update_scope_for_code_search end rescue_from ActiveRecord::QueryCanceled, with: :render_timeout @@ -116,6 +112,9 @@ class SearchController < ApplicationController @ref = params[:project_ref] if params[:project_ref].present? @filter = params[:filter] + # Cache the response on the frontend + expires_in 1.minute + render json: Gitlab::Json.dump(search_autocomplete_opts(term, filter: @filter)) end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index b6aba04c877..8a79353f490 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -36,9 +36,6 @@ class SessionsController < Devise::SessionsController before_action :save_failed_login, if: :action_new_and_failed_login? before_action :load_recaptcha before_action :set_invite_params, only: [:new] - before_action do - push_frontend_feature_flag(:webauthn) - end after_action :log_failed_login, if: :action_new_and_failed_login? after_action :verify_known_sign_in, only: [:create] @@ -56,7 +53,7 @@ class SessionsController < Devise::SessionsController # token mismatch. protect_from_forgery with: :exception, prepend: true, except: :destroy - feature_category :authentication_and_authorization + feature_category :system_access urgency :low CAPTCHA_HEADER = 'X-GitLab-Show-Login-Captcha' @@ -72,8 +69,7 @@ class SessionsController < Devise::SessionsController super do |resource| # User has successfully signed in, so clear any unused reset token if resource.reset_password_token.present? - resource.update(reset_password_token: nil, - reset_password_sent_at: nil) + resource.update(reset_password_token: nil, reset_password_sent_at: nil) end if resource.deactivated? @@ -311,10 +307,8 @@ class SessionsController < Devise::SessionsController def authentication_method if user_params[:otp_attempt] AuthenticationEvent::TWO_FACTOR - elsif user_params[:device_response] && Feature.enabled?(:webauthn) + elsif user_params[:device_response] AuthenticationEvent::TWO_FACTOR_WEBAUTHN - elsif user_params[:device_response] && !Feature.enabled?(:webauthn) - AuthenticationEvent::TWO_FACTOR_U2F else AuthenticationEvent::STANDARD end diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index e81868faa6e..3f20e1c0e86 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -14,7 +14,7 @@ class SnippetsController < Snippets::ApplicationController skip_before_action :authenticate_user!, only: [:index, :show, :raw] - layout 'snippets' + layout :determine_layout def index if params[:username].present? @@ -48,4 +48,12 @@ class SnippetsController < Snippets::ApplicationController def spammable_path snippet_path(@snippet) end + + def determine_layout + if action_name == 'show' && @snippet.author != current_user + 'explore' + else + 'snippets' + end + end end diff --git a/app/controllers/time_tracking/timelogs_controller.rb b/app/controllers/time_tracking/timelogs_controller.rb new file mode 100644 index 00000000000..a2cac071796 --- /dev/null +++ b/app/controllers/time_tracking/timelogs_controller.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module TimeTracking + class TimelogsController < ApplicationController + feature_category :team_planning + urgency :low + + def index + render_404 unless Feature.enabled?(:global_time_tracking_report, current_user) + end + end +end diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index ea99aa12350..1a966739401 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -16,6 +16,7 @@ class UploadsController < ApplicationController "projects/topic" => Projects::Topic, 'alert_management_metric_image' => ::AlertManagement::MetricImage, "achievements/achievement" => Achievements::Achievement, + "abuse_report" => AbuseReport, nil => PersonalSnippet }.freeze diff --git a/app/controllers/users/pins_controller.rb b/app/controllers/users/pins_controller.rb new file mode 100644 index 00000000000..81709dd4a2b --- /dev/null +++ b/app/controllers/users/pins_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Users + class PinsController < ApplicationController + feature_category :navigation + respond_to :json + + def update + panel = pins_params[:panel] + pinned_nav_items = current_user.pinned_nav_items.merge({ panel => pins_params[:menu_item_ids] }) + if current_user.update(pinned_nav_items: pinned_nav_items) + render json: current_user.pinned_nav_items[panel].to_json + else + head :bad_request + end + end + + private + + def pins_params + params.permit(:panel, menu_item_ids: []) + end + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 9546f71cd37..4db5745c005 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -9,20 +9,21 @@ class UsersController < ApplicationController include Gitlab::NoteableMetadata requires_cross_project_access show: false, - groups: false, - projects: false, - contributed: false, - snippets: true, - calendar: false, - followers: false, - following: false, - calendar_activities: true + groups: false, + projects: false, + contributed: false, + snippets: true, + calendar: false, + followers: false, + following: false, + calendar_activities: true skip_before_action :authenticate_user! prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) } before_action :user, except: [:exists] - before_action :authorize_read_user_profile!, - only: [:calendar, :calendar_activities, :groups, :projects, :contributed, :starred, :snippets, :followers, :following] + before_action :authorize_read_user_profile!, only: [ + :calendar, :calendar_activities, :groups, :projects, :contributed, :starred, :snippets, :followers, :following + ] before_action only: [:exists] do check_rate_limit!(:username_exists, scope: request.ip) end @@ -71,7 +72,19 @@ class UsersController < ApplicationController format.json do load_events - pager_json("events/_events", @events.count, events: @events) + + if Feature.enabled?(:profile_tabs_vue, current_user) + @events = if user.include_private_contributions? + @events + else + @events.select { |event| event.visible_to_user?(current_user) } + end + + render json: ::Profile::EventSerializer.new(current_user: current_user, target_user: user) + .represent(@events) + else + pager_json("events/_events", @events.count, events: @events) + end end end end @@ -169,7 +182,7 @@ class UsersController < ApplicationController def exists if Gitlab::CurrentSettings.signup_enabled? || current_user - render json: { exists: !!Namespace.find_by_path_or_name(params[:username]) } + render json: { exists: !!Namespace.without_project_namespaces.find_by_path_or_name(params[:username]) } else render json: { error: _('You must be authenticated to access this path.') }, status: :unauthorized end @@ -178,7 +191,12 @@ class UsersController < ApplicationController def follow followee = current_user.follow(user) - flash[:alert] = followee.errors.full_messages.join(', ') if followee&.errors&.any? + if followee + flash[:alert] = followee.errors.full_messages.join(', ') if followee&.errors&.any? + else + flash[:alert] = s_('Action not allowed.') + end + redirect_path = referer_path(request) || @user redirect_to redirect_path diff --git a/app/controllers/web_ide/remote_ide_controller.rb b/app/controllers/web_ide/remote_ide_controller.rb index fe70e78b1e5..90652a1b6e2 100644 --- a/app/controllers/web_ide/remote_ide_controller.rb +++ b/app/controllers/web_ide/remote_ide_controller.rb @@ -4,7 +4,7 @@ require 'uri' module WebIde class RemoteIdeController < ApplicationController - include VSCodeCDNCSP + include WebIdeCSP rescue_from URI::InvalidComponentError, with: :render_404 |