diff options
Diffstat (limited to 'app/controllers')
252 files changed, 1574 insertions, 422 deletions
diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index 7d8016f763d..5e613c47fc5 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -3,6 +3,8 @@ class AbuseReportsController < ApplicationController before_action :set_user, only: [:new] + feature_category :users + def new @abuse_report = AbuseReport.new @abuse_report.user_id = @user.id diff --git a/app/controllers/admin/abuse_reports_controller.rb b/app/controllers/admin/abuse_reports_controller.rb index 31d825c235b..6f80ed3c172 100644 --- a/app/controllers/admin/abuse_reports_controller.rb +++ b/app/controllers/admin/abuse_reports_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::AbuseReportsController < Admin::ApplicationController + feature_category :users + def index @abuse_reports = AbuseReportsFinder.new(params).execute end diff --git a/app/controllers/admin/appearances_controller.rb b/app/controllers/admin/appearances_controller.rb index 8405f2a5cf8..c2614a158b7 100644 --- a/app/controllers/admin/appearances_controller.rb +++ b/app/controllers/admin/appearances_controller.rb @@ -3,6 +3,8 @@ class Admin::AppearancesController < Admin::ApplicationController before_action :set_appearance, except: :create + feature_category :navigation + def show end diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 73f71f7ad55..786ba73a96f 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -2,6 +2,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController include InternalRedirect + include ServicesHelper # NOTE: Use @application_setting in this controller when you need to access # application_settings after it has been modified. This is because the @@ -16,6 +17,24 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController push_frontend_feature_flag(:ci_instance_variables_ui, default_enabled: true) end + feature_category :not_owned, [ + :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 + ] + + feature_category :source_code_management, [:repository, :clear_repository_check_states] + feature_category :continuous_integration, [:ci_cd, :reset_registration_token] + feature_category :collection, [:usage_data] + feature_category :integrations, [:integrations] + feature_category :pages, [:lets_encrypt_terms_of_service] + VALID_SETTING_PANELS = %w(general repository ci_cd reporting metrics_and_profiling network preferences).freeze @@ -32,6 +51,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController end def integrations + return not_found unless instance_level_integrations? + @integrations = Service.find_or_initialize_all(Service.for_instance).sort_by(&:title) end diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb index c017ecee054..449aa90b0e6 100644 --- a/app/controllers/admin/applications_controller.rb +++ b/app/controllers/admin/applications_controller.rb @@ -6,6 +6,8 @@ class Admin::ApplicationsController < Admin::ApplicationController before_action :set_application, only: [:show, :edit, :update, :destroy] before_action :load_scopes, only: [:new, :create, :edit, :update] + feature_category :authentication_and_authorization + def index applications = ApplicationsFinder.new.execute @applications = Kaminari.paginate_array(applications).page(params[:page]) diff --git a/app/controllers/admin/background_jobs_controller.rb b/app/controllers/admin/background_jobs_controller.rb index fc877142418..d4b906d5e33 100644 --- a/app/controllers/admin/background_jobs_controller.rb +++ b/app/controllers/admin/background_jobs_controller.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true class Admin::BackgroundJobsController < Admin::ApplicationController + feature_category :not_owned end diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb index 3233c765941..4660b0bfbb0 100644 --- a/app/controllers/admin/broadcast_messages_controller.rb +++ b/app/controllers/admin/broadcast_messages_controller.rb @@ -5,6 +5,8 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController before_action :finder, only: [:edit, :update, :destroy] + feature_category :navigation + # rubocop: disable CodeReuse/ActiveRecord def index @broadcast_messages = BroadcastMessage.order(ends_at: :desc).page(params[:page]) diff --git a/app/controllers/admin/ci/variables_controller.rb b/app/controllers/admin/ci/variables_controller.rb index ca9b393550d..f30ee37fa58 100644 --- a/app/controllers/admin/ci/variables_controller.rb +++ b/app/controllers/admin/ci/variables_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::Ci::VariablesController < Admin::ApplicationController + feature_category :continuous_integration + def show respond_to do |format| format.json { render_instance_variables } diff --git a/app/controllers/admin/cohorts_controller.rb b/app/controllers/admin/cohorts_controller.rb index e3df98b7917..d5cd9c55422 100644 --- a/app/controllers/admin/cohorts_controller.rb +++ b/app/controllers/admin/cohorts_controller.rb @@ -5,6 +5,8 @@ class Admin::CohortsController < Admin::ApplicationController track_unique_visits :index, target_id: 'i_analytics_cohorts' + feature_category :instance_statistics + def index if Gitlab::CurrentSettings.usage_ping_enabled cohorts_results = Rails.cache.fetch('cohorts', expires_in: 1.day) do diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index b7b535e70df..7d981d67840 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -6,6 +6,8 @@ class Admin::DashboardController < Admin::ApplicationController COUNTED_ITEMS = [Project, User, Group].freeze + feature_category :not_owned + # rubocop: disable CodeReuse/ActiveRecord def index @counts = Gitlab::Database::Count.approximate_counts(COUNTED_ITEMS) diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb index 180f7d4c803..ed63e65d4df 100644 --- a/app/controllers/admin/deploy_keys_controller.rb +++ b/app/controllers/admin/deploy_keys_controller.rb @@ -4,6 +4,8 @@ class Admin::DeployKeysController < Admin::ApplicationController before_action :deploy_keys, only: [:index] before_action :deploy_key, only: [:destroy, :edit, :update] + feature_category :continuous_delivery + def index end diff --git a/app/controllers/admin/dev_ops_report_controller.rb b/app/controllers/admin/dev_ops_report_controller.rb index bed0d51c331..59b2200fb59 100644 --- a/app/controllers/admin/dev_ops_report_controller.rb +++ b/app/controllers/admin/dev_ops_report_controller.rb @@ -5,6 +5,8 @@ class Admin::DevOpsReportController < Admin::ApplicationController track_unique_visits :show, target_id: 'i_analytics_dev_ops_score' + feature_category :devops_reports + # rubocop: disable CodeReuse/ActiveRecord def show @metric = DevOpsReport::Metric.order(:created_at).last&.present diff --git a/app/controllers/admin/gitaly_servers_controller.rb b/app/controllers/admin/gitaly_servers_controller.rb index 0a5566bfe70..827791c8a4a 100644 --- a/app/controllers/admin/gitaly_servers_controller.rb +++ b/app/controllers/admin/gitaly_servers_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::GitalyServersController < Admin::ApplicationController + feature_category :gitaly + def index @gitaly_servers = Gitaly::Server.all end diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 6414792dd43..032e449f995 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -5,6 +5,8 @@ class Admin::GroupsController < Admin::ApplicationController before_action :group, only: [:edit, :update, :destroy, :project_update, :members_update] + feature_category :subgroups + def index @groups = groups.sort_by_attribute(@sort = params[:sort]) @groups = @groups.search(params[:name]) if params[:name].present? diff --git a/app/controllers/admin/health_check_controller.rb b/app/controllers/admin/health_check_controller.rb index 7668c799cba..e013b5fbd72 100644 --- a/app/controllers/admin/health_check_controller.rb +++ b/app/controllers/admin/health_check_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::HealthCheckController < Admin::ApplicationController + feature_category :not_owned + def show @errors = HealthCheck::Utils.process_checks(checks) end diff --git a/app/controllers/admin/hook_logs_controller.rb b/app/controllers/admin/hook_logs_controller.rb index 8301b3aa880..444ad17f86d 100644 --- a/app/controllers/admin/hook_logs_controller.rb +++ b/app/controllers/admin/hook_logs_controller.rb @@ -8,6 +8,8 @@ class Admin::HookLogsController < Admin::ApplicationController respond_to :html + feature_category :integrations + def show end diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index 51b0f45c5be..ca24f671b9d 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -5,6 +5,8 @@ class Admin::HooksController < Admin::ApplicationController before_action :hook_logs, only: :edit + feature_category :integrations + def index @hooks = SystemHook.all @hook = SystemHook.new @@ -34,7 +36,7 @@ class Admin::HooksController < Admin::ApplicationController end def destroy - hook.destroy + destroy_hook(hook) redirect_to admin_hooks_path, status: :found end diff --git a/app/controllers/admin/identities_controller.rb b/app/controllers/admin/identities_controller.rb index 327538f1e93..dcec50e882d 100644 --- a/app/controllers/admin/identities_controller.rb +++ b/app/controllers/admin/identities_controller.rb @@ -4,6 +4,8 @@ class Admin::IdentitiesController < Admin::ApplicationController before_action :user before_action :identity, except: [:index, :new, :create] + feature_category :authentication_and_authorization + def new @identity = Identity.new end diff --git a/app/controllers/admin/impersonation_tokens_controller.rb b/app/controllers/admin/impersonation_tokens_controller.rb index c35619a944e..c3166d5dd82 100644 --- a/app/controllers/admin/impersonation_tokens_controller.rb +++ b/app/controllers/admin/impersonation_tokens_controller.rb @@ -3,6 +3,8 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController before_action :user + feature_category :authentication_and_authorization + def index set_index_vars end diff --git a/app/controllers/admin/impersonations_controller.rb b/app/controllers/admin/impersonations_controller.rb index 65fe22bd8f4..6c45b03455e 100644 --- a/app/controllers/admin/impersonations_controller.rb +++ b/app/controllers/admin/impersonations_controller.rb @@ -4,6 +4,8 @@ class Admin::ImpersonationsController < Admin::ApplicationController skip_before_action :authenticate_admin! before_action :authenticate_impersonator! + feature_category :authentication_and_authorization + def destroy original_user = stop_impersonation redirect_to admin_user_path(original_user), status: :found diff --git a/app/controllers/admin/instance_review_controller.rb b/app/controllers/admin/instance_review_controller.rb new file mode 100644 index 00000000000..db304c82dd6 --- /dev/null +++ b/app/controllers/admin/instance_review_controller.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true +class Admin::InstanceReviewController < Admin::ApplicationController + feature_category :instance_statistics + + def index + redirect_to("#{::Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL}/instance_review?#{instance_review_params}") + end + + def instance_review_params + result = { + instance_review: { + email: current_user.email, + last_name: current_user.name, + version: ::Gitlab::VERSION + } + } + + if Gitlab::CurrentSettings.usage_ping_enabled? + data = ::Gitlab::UsageData.data + counts = data[:counts] + + result[:instance_review].merge!( + users_count: data[:active_user_count], + projects_count: counts[:projects], + groups_count: counts[:groups], + issues_count: counts[:issues], + merge_requests_count: counts[:merge_requests], + internal_pipelines_count: counts[:ci_internal_pipelines], + external_pipelines_count: counts[:ci_external_pipelines], + labels_count: counts[:labels], + milestones_count: counts[:milestones], + snippets_count: counts[:snippets], + notes_count: counts[:notes] + ) + end + + result.to_query + end +end diff --git a/app/controllers/admin/instance_statistics_controller.rb b/app/controllers/admin/instance_statistics_controller.rb index 3aee26b97a2..dfbd704cb0c 100644 --- a/app/controllers/admin/instance_statistics_controller.rb +++ b/app/controllers/admin/instance_statistics_controller.rb @@ -7,6 +7,8 @@ class Admin::InstanceStatisticsController < Admin::ApplicationController track_unique_visits :index, target_id: 'i_analytics_instance_statistics' + feature_category :instance_statistics + def index end diff --git a/app/controllers/admin/integrations_controller.rb b/app/controllers/admin/integrations_controller.rb index 1e2a99f7078..9a1d5a11f7f 100644 --- a/app/controllers/admin/integrations_controller.rb +++ b/app/controllers/admin/integrations_controller.rb @@ -2,6 +2,9 @@ class Admin::IntegrationsController < Admin::ApplicationController include IntegrationsActions + include ServicesHelper + + feature_category :integrations private @@ -10,7 +13,7 @@ class Admin::IntegrationsController < Admin::ApplicationController end def integrations_enabled? - true + instance_level_integrations? end def scoped_edit_integration_path(integration) diff --git a/app/controllers/admin/jobs_controller.rb b/app/controllers/admin/jobs_controller.rb index 7b50a45a9cd..b800ca79d6b 100644 --- a/app/controllers/admin/jobs_controller.rb +++ b/app/controllers/admin/jobs_controller.rb @@ -3,6 +3,8 @@ class Admin::JobsController < Admin::ApplicationController BUILDS_PER_PAGE = 30 + feature_category :continuous_integration + def index # We need all builds for tabs counters @all_builds = Ci::JobsFinder.new(current_user: current_user).execute diff --git a/app/controllers/admin/keys_controller.rb b/app/controllers/admin/keys_controller.rb index 58ea19d1210..03383604e30 100644 --- a/app/controllers/admin/keys_controller.rb +++ b/app/controllers/admin/keys_controller.rb @@ -3,6 +3,8 @@ class Admin::KeysController < Admin::ApplicationController before_action :user, only: [:show, :destroy] + feature_category :authentication_and_authorization + def show @key = user.keys.find(params[:id]) diff --git a/app/controllers/admin/labels_controller.rb b/app/controllers/admin/labels_controller.rb index 6cb206c1686..be63bf4c7ce 100644 --- a/app/controllers/admin/labels_controller.rb +++ b/app/controllers/admin/labels_controller.rb @@ -3,6 +3,8 @@ class Admin::LabelsController < Admin::ApplicationController before_action :set_label, only: [:show, :edit, :update, :destroy] + feature_category :issue_tracking + def index @labels = Label.templates.page(params[:page]) end diff --git a/app/controllers/admin/plan_limits_controller.rb b/app/controllers/admin/plan_limits_controller.rb index 2620db8aec5..0a5cdc06d61 100644 --- a/app/controllers/admin/plan_limits_controller.rb +++ b/app/controllers/admin/plan_limits_controller.rb @@ -5,6 +5,8 @@ class Admin::PlanLimitsController < Admin::ApplicationController before_action :set_plan_limits + feature_category :not_owned + def create redirect_path = referer_path(request) || general_admin_application_settings_path diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 9fe1f22c342..c4564478462 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -6,6 +6,9 @@ class Admin::ProjectsController < Admin::ApplicationController before_action :project, only: [:show, :transfer, :repository_check, :destroy] before_action :group, only: [:show, :transfer] + feature_category :projects, [:index, :show, :transfer, :destroy] + feature_category :source_code_management, [:repository_check] + def index params[:sort] ||= 'latest_activity_desc' @sort = params[:sort] diff --git a/app/controllers/admin/requests_profiles_controller.rb b/app/controllers/admin/requests_profiles_controller.rb index 24383455064..fbbe8c24637 100644 --- a/app/controllers/admin/requests_profiles_controller.rb +++ b/app/controllers/admin/requests_profiles_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::RequestsProfilesController < Admin::ApplicationController + feature_category :not_owned + def index @profile_token = Gitlab::RequestProfiler.profile_token @profiles = Gitlab::RequestProfiler.all.group_by(&:request_path) diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb index 774ce04d079..7761ffaac84 100644 --- a/app/controllers/admin/runner_projects_controller.rb +++ b/app/controllers/admin/runner_projects_controller.rb @@ -3,6 +3,8 @@ class Admin::RunnerProjectsController < Admin::ApplicationController before_action :project, only: [:create] + feature_category :continuous_integration + def create @runner = Ci::Runner.find(params[:runner_project][:runner_id]) diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb index 7a377a33d41..576b148fbff 100644 --- a/app/controllers/admin/runners_controller.rb +++ b/app/controllers/admin/runners_controller.rb @@ -1,7 +1,11 @@ # frozen_string_literal: true class Admin::RunnersController < Admin::ApplicationController - before_action :runner, except: [:index, :tag_list] + include RunnerSetupScripts + + before_action :runner, except: [:index, :tag_list, :runner_setup_scripts] + + feature_category :continuous_integration def index finder = Ci::RunnersFinder.new(current_user: current_user, params: params) @@ -53,6 +57,10 @@ class Admin::RunnersController < Admin::ApplicationController render json: ActsAsTaggableOn::TagSerializer.new.represent(tags) end + def runner_setup_scripts + private_runner_setup_scripts + end + private def runner diff --git a/app/controllers/admin/serverless/domains_controller.rb b/app/controllers/admin/serverless/domains_controller.rb index 1d4f10e033f..49cd9f7a36d 100644 --- a/app/controllers/admin/serverless/domains_controller.rb +++ b/app/controllers/admin/serverless/domains_controller.rb @@ -4,6 +4,8 @@ class Admin::Serverless::DomainsController < Admin::ApplicationController before_action :check_feature_flag before_action :domain, only: [:update, :verify, :destroy] + feature_category :serverless + def index @domain = PagesDomain.instance_serverless.first_or_initialize end diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb index 1f4250639c4..379e74bb249 100644 --- a/app/controllers/admin/services_controller.rb +++ b/app/controllers/admin/services_controller.rb @@ -6,6 +6,8 @@ class Admin::ServicesController < Admin::ApplicationController before_action :service, only: [:edit, :update] before_action :whitelist_query_limiting, only: [:index] + feature_category :integrations + def index @services = Service.find_or_create_templates.sort_by(&:title) @existing_instance_types = Service.for_instance.pluck(:type) # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/controllers/admin/sessions_controller.rb b/app/controllers/admin/sessions_controller.rb index 0c0bbaf4d93..9c378f4c883 100644 --- a/app/controllers/admin/sessions_controller.rb +++ b/app/controllers/admin/sessions_controller.rb @@ -1,12 +1,14 @@ # frozen_string_literal: true class Admin::SessionsController < ApplicationController - include Authenticates2FAForAdminMode + include AuthenticatesWithTwoFactorForAdminMode include InternalRedirect include RendersLdapServers before_action :user_is_admin! + feature_category :authentication_and_authorization + def new if current_user_mode.admin_mode? redirect_to redirect_path, notice: _('Admin mode already enabled') @@ -65,7 +67,10 @@ class Admin::SessionsController < ApplicationController end def valid_otp_attempt?(user) - valid_otp_attempt = user.validate_and_consume_otp!(user_params[:otp_attempt]) + otp_validation_result = + ::Users::ValidateOtpService.new(user).execute(user_params[:otp_attempt]) + valid_otp_attempt = otp_validation_result[:status] == :success + return valid_otp_attempt if Gitlab::Database.read_only? valid_otp_attempt || user.invalidate_otp_backup_code!(user_params[:otp_attempt]) diff --git a/app/controllers/admin/spam_logs_controller.rb b/app/controllers/admin/spam_logs_controller.rb index 689e502a221..67d991c8b03 100644 --- a/app/controllers/admin/spam_logs_controller.rb +++ b/app/controllers/admin/spam_logs_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::SpamLogsController < Admin::ApplicationController + feature_category :not_owned + # rubocop: disable CodeReuse/ActiveRecord def index @spam_logs = SpamLog.order(id: :desc).page(params[:page]) diff --git a/app/controllers/admin/system_info_controller.rb b/app/controllers/admin/system_info_controller.rb index 657aa177ecf..f14305528a3 100644 --- a/app/controllers/admin/system_info_controller.rb +++ b/app/controllers/admin/system_info_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Admin::SystemInfoController < Admin::ApplicationController + feature_category :not_owned + EXCLUDED_MOUNT_OPTIONS = %w[ nobrowse read-only diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index e19b09e1324..bd7b69384b2 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -6,10 +6,14 @@ class Admin::UsersController < Admin::ApplicationController before_action :user, except: [:index, :new, :create] before_action :check_impersonation_availability, only: :impersonate before_action :ensure_destroy_prerequisites_met, only: [:destroy] + before_action :check_admin_approval_feature_available!, only: [:approve] + + feature_category :users def index @users = User.filter_items(params[:filter]).order_name_asc @users = @users.search_with_secondary_emails(params[:search_query]) if params[:search_query].present? + @users = @users.includes(:authorized_projects) # rubocop: disable CodeReuse/ActiveRecord @users = @users.sort_by_attribute(@sort = params[:sort]) @users = @users.page(params[:page]) end @@ -59,6 +63,16 @@ class Admin::UsersController < Admin::ApplicationController end end + def approve + result = Users::ApproveService.new(current_user).execute(user) + + if result[:status] == :success + redirect_back_or_admin_user(notice: _("Successfully approved")) + else + redirect_back_or_admin_user(alert: result[:message]) + end + end + def activate return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user must be unblocked to be activated")) if user.blocked? @@ -69,6 +83,7 @@ class Admin::UsersController < Admin::ApplicationController def deactivate return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user cannot be deactivated")) if user.blocked? 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? return redirect_back_or_admin_user(notice: _("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated") % { minimum_inactive_days: ::User::MINIMUM_INACTIVE_DAYS }) unless user.can_be_deactivated? user.deactivate @@ -78,7 +93,7 @@ class Admin::UsersController < Admin::ApplicationController def block result = Users::BlockService.new(current_user).execute(user) - if result[:status] = :success + 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")) @@ -168,7 +183,7 @@ class Admin::UsersController < Admin::ApplicationController # restore username to keep form action url. user.username = params[:id] format.html { render "edit" } - format.json { render json: [result[:message]], status: result[:status] } + format.json { render json: [result[:message]], status: :internal_server_error } end end end @@ -283,6 +298,10 @@ class Admin::UsersController < Admin::ApplicationController def log_impersonation_event Gitlab::AppLogger.info(_("User %{current_user_username} has started impersonating %{username}") % { current_user_username: current_user.username, username: user.username }) end + + def check_admin_approval_feature_available! + access_denied! unless Feature.enabled?(:admin_approval_for_new_user_signups, default_enabled: true) + end end Admin::UsersController.prepend_if_ee('EE::Admin::UsersController') diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5f05337e59e..05f496c3b99 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -271,6 +271,7 @@ class ApplicationController < ActionController::Base headers['X-XSS-Protection'] = '1; mode=block' headers['X-UA-Compatible'] = 'IE=edge' headers['X-Content-Type-Options'] = 'nosniff' + headers[Gitlab::Metrics::RequestsRackMiddleware::FEATURE_CATEGORY_HEADER] = feature_category end def default_cache_headers @@ -465,7 +466,8 @@ class ApplicationController < ActionController::Base user: -> { auth_user if strong_memoized?(:auth_user) }, project: -> { @project if @project&.persisted? }, namespace: -> { @group if @group&.persisted? }, - caller_id: full_action_name) do + caller_id: caller_id, + feature_category: feature_category) do yield ensure @current_context = Labkit::Context.current.to_h @@ -484,7 +486,7 @@ class ApplicationController < ActionController::Base def set_page_title_header # Per https://tools.ietf.org/html/rfc5987, headers need to be ISO-8859-1, not UTF-8 - response.headers['Page-Title'] = URI.escape(page_title('GitLab')) + response.headers['Page-Title'] = Addressable::URI.encode_component(page_title('GitLab')) end def set_current_admin(&block) @@ -547,10 +549,14 @@ class ApplicationController < ActionController::Base end end - def full_action_name + def caller_id "#{self.class.name}##{action_name}" end + def feature_category + self.class.feature_category_for_action(action_name).to_s + end + def required_signup_info return unless current_user return unless current_user.role_required? diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 99fa17e202a..ac4ee14c6a9 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -3,6 +3,12 @@ class AutocompleteController < ApplicationController skip_before_action :authenticate_user!, only: [:users, :award_emojis, :merge_request_target_branches] + feature_category :users, [:users, :user] + feature_category :projects, [:projects] + feature_category :issue_tracking, [:award_emojis] + feature_category :code_review, [:merge_request_target_branches] + feature_category :continuous_delivery, [:deploy_keys_with_owners] + def users group = Autocomplete::GroupFinder .new(current_user, project, params) diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb index a18c80b996e..f5a9b9b61db 100644 --- a/app/controllers/boards/issues_controller.rb +++ b/app/controllers/boards/issues_controller.rb @@ -21,6 +21,8 @@ module Boards before_action :validate_id_list, only: [:bulk_move] before_action :can_move_issues?, only: [:bulk_move] + feature_category :boards + def index list_service = Boards::Issues::ListService.new(board_parent, current_user, filter_params) issues = issues_from(list_service) diff --git a/app/controllers/boards/lists_controller.rb b/app/controllers/boards/lists_controller.rb index 0b8469e8290..aecd287370f 100644 --- a/app/controllers/boards/lists_controller.rb +++ b/app/controllers/boards/lists_controller.rb @@ -8,6 +8,8 @@ module Boards before_action :authorize_read_list, only: [:index] skip_before_action :authenticate_user!, only: [:index] + feature_category :boards + def index lists = Boards::Lists::ListService.new(board.resource_parent, current_user).execute(board) @@ -42,7 +44,7 @@ module Boards list = board.lists.destroyable.find(params[:id]) service = Boards::Lists::DestroyService.new(board_parent, current_user) - if service.execute(list) + if service.execute(list).success? head :ok else head :unprocessable_entity diff --git a/app/controllers/clusters/base_controller.rb b/app/controllers/clusters/base_controller.rb index 188805c6106..b1ffdf00b87 100644 --- a/app/controllers/clusters/base_controller.rb +++ b/app/controllers/clusters/base_controller.rb @@ -8,6 +8,8 @@ class Clusters::BaseController < ApplicationController helper_method :clusterable + feature_category :kubernetes_management + private def cluster diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb index 7006c23321c..52719e90e04 100644 --- a/app/controllers/clusters/clusters_controller.rb +++ b/app/controllers/clusters/clusters_controller.rb @@ -180,13 +180,20 @@ class Clusters::ClustersController < Clusters::BaseController params.permit(:cleanup) end + def base_permitted_cluster_params + [ + :enabled, + :environment_scope, + :managed, + :namespace_per_environment + ] + end + def update_params if cluster.provided_by_user? params.require(:cluster).permit( - :enabled, + *base_permitted_cluster_params, :name, - :environment_scope, - :managed, :base_domain, :management_project_id, platform_kubernetes_attributes: [ @@ -198,9 +205,7 @@ class Clusters::ClustersController < Clusters::BaseController ) else params.require(:cluster).permit( - :enabled, - :environment_scope, - :managed, + *base_permitted_cluster_params, :base_domain, :management_project_id, platform_kubernetes_attributes: [ @@ -212,10 +217,8 @@ class Clusters::ClustersController < Clusters::BaseController def create_gcp_cluster_params params.require(:cluster).permit( - :enabled, + *base_permitted_cluster_params, :name, - :environment_scope, - :managed, provider_gcp_attributes: [ :gcp_project_id, :zone, @@ -232,10 +235,8 @@ class Clusters::ClustersController < Clusters::BaseController def create_aws_cluster_params params.require(:cluster).permit( - :enabled, + *base_permitted_cluster_params, :name, - :environment_scope, - :managed, provider_aws_attributes: [ :kubernetes_version, :key_name, @@ -255,10 +256,8 @@ class Clusters::ClustersController < Clusters::BaseController def create_user_cluster_params params.require(:cluster).permit( - :enabled, + *base_permitted_cluster_params, :name, - :environment_scope, - :managed, platform_kubernetes_attributes: [ :namespace, :api_url, diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb index 9ff97f398f5..5c74d79951f 100644 --- a/app/controllers/concerns/authenticates_with_two_factor.rb +++ b/app/controllers/concerns/authenticates_with_two_factor.rb @@ -89,10 +89,7 @@ module AuthenticatesWithTwoFactor user.save! sign_in(user, message: :two_factor_authenticated, event: :authentication) else - user.increment_failed_attempts! - Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=OTP") - flash.now[:alert] = _('Invalid two-factor code.') - prompt_for_two_factor(user) + handle_two_factor_failure(user, 'OTP', _('Invalid two-factor code.')) end end @@ -101,7 +98,7 @@ module AuthenticatesWithTwoFactor 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') + handle_two_factor_failure(user, 'U2F', _('Authentication via U2F device failed.')) end end @@ -109,7 +106,7 @@ module AuthenticatesWithTwoFactor if Webauthn::AuthenticateService.new(user, user_params[:device_response], session[:challenge]).execute handle_two_factor_success(user) else - handle_two_factor_failure(user, 'WebAuthn') + handle_two_factor_failure(user, 'WebAuthn', _('Authentication via WebAuthn device failed.')) end end @@ -152,13 +149,19 @@ module AuthenticatesWithTwoFactor sign_in(user, message: :two_factor_authenticated, event: :authentication) end - def handle_two_factor_failure(user, method) + def handle_two_factor_failure(user, method, message) user.increment_failed_attempts! + log_failed_two_factor(user, method, request.remote_ip) + Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=#{method}") - flash.now[:alert] = _('Authentication via %{method} device failed.') % { method: method } + flash.now[:alert] = message prompt_for_two_factor(user) end + def log_failed_two_factor(user, method, ip_address) + # overridden in EE + end + def handle_changed_user(user) clear_two_factor_attempt! @@ -173,3 +176,5 @@ module AuthenticatesWithTwoFactor Digest::SHA256.hexdigest(user.encrypted_password) != session[:user_password_hash] end end + +AuthenticatesWithTwoFactor.prepend_if_ee('EE::AuthenticatesWithTwoFactor') diff --git a/app/controllers/admin/concerns/authenticates_2fa_for_admin_mode.rb b/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb index 03783cd75a3..a8155f1e639 100644 --- a/app/controllers/admin/concerns/authenticates_2fa_for_admin_mode.rb +++ b/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -module Authenticates2FAForAdminMode +module AuthenticatesWithTwoFactorForAdminMode extend ActiveSupport::Concern included do @@ -52,11 +52,7 @@ module Authenticates2FAForAdminMode # The admin user has successfully passed 2fa, enable admin mode ignoring password enable_admin_mode else - user.increment_failed_attempts! - Gitlab::AppLogger.info("Failed Admin Mode Login: user=#{user.username} ip=#{request.remote_ip} method=OTP") - flash.now[:alert] = _('Invalid two-factor code.') - - admin_mode_prompt_for_two_factor(user) + admin_handle_two_factor_failure(user, 'OTP', _('Invalid two-factor code.')) end end @@ -64,7 +60,7 @@ module Authenticates2FAForAdminMode 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') + admin_handle_two_factor_failure(user, 'U2F', _('Authentication via U2F device failed.')) end end @@ -72,7 +68,7 @@ module Authenticates2FAForAdminMode if Webauthn::AuthenticateService.new(user, user_params[:device_response], session[:challenge]).execute admin_handle_two_factor_success else - admin_handle_two_factor_failure(user, 'WebAuthn') + admin_handle_two_factor_failure(user, 'WebAuthn', _('Authentication via WebAuthn device failed.')) end end @@ -100,11 +96,12 @@ module Authenticates2FAForAdminMode enable_admin_mode end - def admin_handle_two_factor_failure(user, method) + def admin_handle_two_factor_failure(user, method, message) user.increment_failed_attempts! - Gitlab::AppLogger.info("Failed Admin Mode Login: user=#{user.username} ip=#{request.remote_ip} method=#{method}") - flash.now[:alert] = _('Authentication via %{method} device failed.') % { method: method } + log_failed_two_factor(user, method, request.remote_ip) + Gitlab::AppLogger.info("Failed Admin Mode Login: user=#{user.username} ip=#{request.remote_ip} method=#{method}") + flash.now[:alert] = message admin_mode_prompt_for_two_factor(user) end end diff --git a/app/controllers/concerns/boards_actions.rb b/app/controllers/concerns/boards_actions.rb index 9d40b9e8c88..b382e338a78 100644 --- a/app/controllers/concerns/boards_actions.rb +++ b/app/controllers/concerns/boards_actions.rb @@ -9,7 +9,7 @@ module BoardsActions before_action :boards, only: :index before_action :board, only: :show - before_action :push_wip_limits, only: [:index, :show] + before_action :push_licensed_features, only: [:index, :show] before_action do push_frontend_feature_flag(:not_issuable_queries, parent, default_enabled: true) end @@ -29,7 +29,7 @@ module BoardsActions private # Noop on FOSS - def push_wip_limits + def push_licensed_features end def boards diff --git a/app/controllers/concerns/controller_with_feature_category.rb b/app/controllers/concerns/controller_with_feature_category.rb index f8985cf0950..c1ff9ef2e69 100644 --- a/app/controllers/concerns/controller_with_feature_category.rb +++ b/app/controllers/concerns/controller_with_feature_category.rb @@ -5,35 +5,38 @@ module ControllerWithFeatureCategory include Gitlab::ClassAttributes class_methods do - def feature_category(category, config = {}) - validate_config!(config) + def feature_category(category, actions = []) + feature_category_configuration[category] ||= [] + feature_category_configuration[category] += actions.map(&:to_s) - category_config = Config.new(category, config[:only], config[:except], config[:if], config[:unless]) - # Add the config to the beginning. That way, the last defined one takes precedence. - feature_category_configuration.unshift(category_config) + validate_config!(feature_category_configuration) end def feature_category_for_action(action) - category_config = feature_category_configuration.find { |config| config.matches?(action) } + category_config = feature_category_configuration.find do |_, actions| + actions.empty? || actions.include?(action) + end - category_config&.category || superclass_feature_category_for_action(action) + category_config&.first || superclass_feature_category_for_action(action) end private def validate_config!(config) - invalid_keys = config.keys - [:only, :except, :if, :unless] - if invalid_keys.any? - raise ArgumentError, "unknown arguments: #{invalid_keys} " + empty = config.find { |_, actions| actions.empty? } + duplicate_actions = config.values.flatten.group_by(&:itself).select { |_, v| v.count > 1 }.keys + + if config.length > 1 && empty + raise ArgumentError, "#{empty.first} is defined for all actions, but other categories are set" end - if config.key?(:only) && config.key?(:except) - raise ArgumentError, "cannot configure both `only` and `except`" + if duplicate_actions.any? + raise ArgumentError, "Actions have multiple feature categories: #{duplicate_actions.join(', ')}" end end def feature_category_configuration - class_attributes[:feature_category_config] ||= [] + class_attributes[:feature_category_config] ||= {} end def superclass_feature_category_for_action(action) diff --git a/app/controllers/concerns/controller_with_feature_category/config.rb b/app/controllers/concerns/controller_with_feature_category/config.rb deleted file mode 100644 index 624691ee4f6..00000000000 --- a/app/controllers/concerns/controller_with_feature_category/config.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module ControllerWithFeatureCategory - class Config - attr_reader :category - - def initialize(category, only, except, if_proc, unless_proc) - @category = category.to_sym - @only, @except = only&.map(&:to_s), except&.map(&:to_s) - @if_proc, @unless_proc = if_proc, unless_proc - end - - def matches?(action) - included?(action) && !excluded?(action) && - if_proc?(action) && !unless_proc?(action) - end - - private - - attr_reader :only, :except, :if_proc, :unless_proc - - def if_proc?(action) - if_proc.nil? || if_proc.call(action) - end - - def unless_proc?(action) - unless_proc.present? && unless_proc.call(action) - end - - def included?(action) - only.nil? || only.include?(action) - end - - def excluded?(action) - except.present? && except.include?(action) - end - end -end diff --git a/app/controllers/concerns/hooks_execution.rb b/app/controllers/concerns/hooks_execution.rb index ad1f8341109..87d215f50e7 100644 --- a/app/controllers/concerns/hooks_execution.rb +++ b/app/controllers/concerns/hooks_execution.rb @@ -5,6 +5,21 @@ module HooksExecution private + def destroy_hook(hook) + result = WebHooks::DestroyService.new(current_user).execute(hook) + + if result[:status] == :success + flash[:notice] = + if result[:async] + _("%{hook_type} was scheduled for deletion") % { hook_type: hook.model_name.human } + else + _("%{hook_type} was deleted") % { hook_type: hook.model_name.human } + end + else + flash[:alert] = result[:message] + end + end + def set_hook_execution_notice(result) http_status = result[:http_status] message = result[:message] diff --git a/app/controllers/concerns/integrations_actions.rb b/app/controllers/concerns/integrations_actions.rb index 6060dc729af..39f63bbaaec 100644 --- a/app/controllers/concerns/integrations_actions.rb +++ b/app/controllers/concerns/integrations_actions.rb @@ -20,7 +20,7 @@ module IntegrationsActions respond_to do |format| format.html do if saved - PropagateIntegrationWorker.perform_async(integration.id, false) + PropagateIntegrationWorker.perform_async(integration.id) redirect_to scoped_edit_integration_path(integration), notice: success_message else render 'shared/integrations/edit' diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 89ba2175b60..0d7af57328a 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -41,10 +41,13 @@ module IssuableCollections end def set_pagination + row_count = finder.row_count + @issuables = @issuables.page(params[:page]) @issuables = per_page_for_relative_position if params[:sort] == 'relative_position' + @issuables = @issuables.without_count if row_count == -1 @issuable_meta_data = Gitlab::IssuableMetadata.new(current_user, @issuables).data - @total_pages = issuable_page_count(@issuables) + @total_pages = page_count_for_relation(@issuables, row_count) end # rubocop:enable Gitlab/ModuleWithInstanceVariables @@ -58,14 +61,11 @@ module IssuableCollections end # rubocop: enable CodeReuse/ActiveRecord - def issuable_page_count(relation) - page_count_for_relation(relation, finder.row_count) - end - def page_count_for_relation(relation, row_count) limit = relation.limit_value.to_f return 1 if limit == 0 + return (params[:page] || 1).to_i + 1 if row_count == -1 (row_count.to_f / limit).ceil end diff --git a/app/controllers/concerns/issuable_collections_action.rb b/app/controllers/concerns/issuable_collections_action.rb index e3ac117660b..7ed66027da3 100644 --- a/app/controllers/concerns/issuable_collections_action.rb +++ b/app/controllers/concerns/issuable_collections_action.rb @@ -59,6 +59,9 @@ module IssuableCollectionsAction end def finder_options - super.merge(non_archived: true) + super.merge( + non_archived: true, + issue_types: Issue::TYPES_FOR_LIST + ) end end diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb index 8c7f156f7f8..816a93f14c6 100644 --- a/app/controllers/concerns/membership_actions.rb +++ b/app/controllers/concerns/membership_actions.rb @@ -22,10 +22,14 @@ module MembershipActions .new(current_user, update_params) .execute(member) - member = present_members([member]).first - - respond_to do |format| - format.js { render 'shared/members/update', locals: { member: member } } + if member.expires? + render json: { + expires_in: helpers.distance_of_time_in_words_to_now(member.expires_at), + expires_soon: member.expires_soon?, + expires_at_formatted: member.expires_at.to_time.in_time_zone.to_s(:medium) + } + else + render json: {} end end @@ -101,7 +105,7 @@ module MembershipActions # rubocop: enable CodeReuse/ActiveRecord def resend_invite - member = membershipable.members.find(params[:id]) + member = membershipable_members.find(params[:id]) if member.invite? member.resend_invite @@ -118,6 +122,10 @@ module MembershipActions raise NotImplementedError end + def membershipable_members + raise NotImplementedError + end + def root_params_key case membershipable when Namespace diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb index 29138e7b014..6470c75dfbd 100644 --- a/app/controllers/concerns/milestone_actions.rb +++ b/app/controllers/concerns/milestone_actions.rb @@ -3,13 +3,25 @@ module MilestoneActions extend ActiveSupport::Concern + def issues + respond_to do |format| + format.html { redirect_to milestone_redirect_path } + format.json do + render json: tabs_json("shared/milestones/_issues_tab", { + issues: @milestone.sorted_issues(current_user), # rubocop:disable Gitlab/ModuleWithInstanceVariables + show_project_name: Gitlab::Utils.to_boolean(params[:show_project_name]) + }) + end + end + end + def merge_requests respond_to do |format| format.html { redirect_to milestone_redirect_path } format.json do render json: tabs_json("shared/milestones/_merge_requests_tab", { merge_requests: @milestone.sorted_merge_requests(current_user), # rubocop:disable Gitlab/ModuleWithInstanceVariables - show_project_name: true + show_project_name: Gitlab::Utils.to_boolean(params[:show_project_name]) }) end end diff --git a/app/controllers/concerns/multiple_boards_actions.rb b/app/controllers/concerns/multiple_boards_actions.rb index 95a6800f55c..370b8c72bfe 100644 --- a/app/controllers/concerns/multiple_boards_actions.rb +++ b/app/controllers/concerns/multiple_boards_actions.rb @@ -21,11 +21,13 @@ module MultipleBoardsActions end def create - board = Boards::CreateService.new(parent, current_user, board_params).execute + response = Boards::CreateService.new(parent, current_user, board_params).execute respond_to do |format| format.json do - if board.persisted? + board = response.payload + + if response.success? extra_json = { board_path: board_path(board) } render json: serialize_as_json(board).merge(extra_json) else diff --git a/app/controllers/concerns/redis_tracking.rb b/app/controllers/concerns/redis_tracking.rb index fa5eef981d1..d81bd10d5bb 100644 --- a/app/controllers/concerns/redis_tracking.rb +++ b/app/controllers/concerns/redis_tracking.rb @@ -11,12 +11,17 @@ # # if the feature flag is enabled by default you should use # track_redis_hll_event :index, :show, name: 'i_analytics_dev_ops_score', feature: :my_feature, feature_default_enabled: true +# +# You can also pass custom conditions using `if:`, using the same format as with Rails callbacks. module RedisTracking extend ActiveSupport::Concern class_methods do - def track_redis_hll_event(*controller_actions, name:, feature:, feature_default_enabled: false) - after_action only: controller_actions, if: -> { request.format.html? && request.headers['DNT'] != '1' } do + def track_redis_hll_event(*controller_actions, name:, feature:, feature_default_enabled: false, if: nil) + custom_conditions = Array.wrap(binding.local_variable_get('if')) + conditions = [:trackable_request?, *custom_conditions] + + after_action only: controller_actions, if: conditions do track_unique_redis_hll_event(name, feature, feature_default_enabled) end end @@ -26,12 +31,15 @@ module RedisTracking def track_unique_redis_hll_event(event_name, feature, feature_default_enabled) return unless metric_feature_enabled?(feature, feature_default_enabled) - return unless Gitlab::CurrentSettings.usage_ping_enabled? return unless visitor_id Gitlab::UsageDataCounters::HLLRedisCounter.track_event(visitor_id, event_name) end + def trackable_request? + request.format.html? && request.headers['DNT'] != '1' + end + def metric_feature_enabled?(feature, default_enabled) Feature.enabled?(feature, default_enabled: default_enabled) end diff --git a/app/controllers/concerns/runner_setup_scripts.rb b/app/controllers/concerns/runner_setup_scripts.rb new file mode 100644 index 00000000000..c0e657a32d1 --- /dev/null +++ b/app/controllers/concerns/runner_setup_scripts.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module RunnerSetupScripts + extend ActiveSupport::Concern + + private + + def private_runner_setup_scripts(**kwargs) + instructions = Gitlab::Ci::RunnerInstructions.new(current_user: current_user, os: script_params[:os], arch: script_params[:arch], **kwargs) + output = { + install: instructions.install_script, + register: instructions.register_command + } + + if instructions.errors.any? + render json: { errors: instructions.errors }, status: :bad_request + else + render json: output + end + end + + def script_params + params.permit(:os, :arch) + end +end diff --git a/app/controllers/concerns/show_inherited_labels_checker.rb b/app/controllers/concerns/show_inherited_labels_checker.rb new file mode 100644 index 00000000000..9847226f599 --- /dev/null +++ b/app/controllers/concerns/show_inherited_labels_checker.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module ShowInheritedLabelsChecker + extend ActiveSupport::Concern + + private + + def show_inherited_labels?(include_ancestor_groups) + Feature.enabled?(:show_inherited_labels, @project || @group, default_enabled: true) || include_ancestor_groups # rubocop:disable Gitlab/ModuleWithInstanceVariables + end +end diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb index 4548595d968..e4c3df6ccc3 100644 --- a/app/controllers/concerns/snippets_actions.rb +++ b/app/controllers/concerns/snippets_actions.rb @@ -17,13 +17,7 @@ module SnippetsActions respond_to :html end - def edit - # We need to load some info from the existing blob - snippet.content = blob.data - snippet.file_name = blob.path - - render 'edit' - end + def edit; end # This endpoint is being replaced by Snippets::BlobController#raw # Support for old raw links will be maintainted via this action but @@ -55,7 +49,6 @@ module SnippetsActions def show respond_to do |format| format.html do - conditionally_expand_blob(blob) @note = Note.new(noteable: @snippet, project: @snippet.project) @noteable = @snippet @@ -80,29 +73,6 @@ module SnippetsActions end end end - - def update - update_params = snippet_params.merge(spammable_params) - - service_response = Snippets::UpdateService.new(@snippet.project, current_user, update_params).execute(@snippet) - @snippet = service_response.payload[:snippet] - - handle_repository_error(:edit) - end - - def destroy - service_response = Snippets::DestroyService.new(current_user, @snippet).execute - - if service_response.success? - redirect_to gitlab_dashboard_snippets_path(@snippet), status: :found - elsif service_response.http_status == 403 - access_denied! - else - redirect_to gitlab_snippet_path(@snippet), - status: :found, - alert: service_response.message - end - end # rubocop:enable Gitlab/ModuleWithInstanceVariables private @@ -124,12 +94,4 @@ module SnippetsActions def convert_line_endings(content) params[:line_ending] == 'raw' ? content : content.gsub(/\r\n/, "\n") end - - def handle_repository_error(action) - errors = Array(snippet.errors.delete(:repository)) - - flash.now[:alert] = errors.first if errors.present? - - recaptcha_check_with_fallback(errors.empty?) { render action } - end end diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb index 5a5b634da40..aed109309e3 100644 --- a/app/controllers/concerns/wiki_actions.rb +++ b/app/controllers/concerns/wiki_actions.rb @@ -5,6 +5,7 @@ module WikiActions include PreviewMarkdown include SendsBlob include Gitlab::Utils::StrongMemoize + include RedisTracking extend ActiveSupport::Concern included do @@ -31,6 +32,11 @@ 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, + feature: :track_unique_wiki_page_views, feature_default_enabled: true + helper_method :view_file_button, :diff_file_html_data end @@ -44,7 +50,7 @@ module WikiActions wiki.list_pages(sort: params[:sort], direction: params[:direction]) ).page(params[:page]) - @wiki_entries = WikiPage.group_by_directory(@wiki_pages) + @wiki_entries = WikiDirectory.group_pages(@wiki_pages) render 'shared/wikis/pages' end diff --git a/app/controllers/confirmations_controller.rb b/app/controllers/confirmations_controller.rb index a27c4027380..c42c9827eaf 100644 --- a/app/controllers/confirmations_controller.rb +++ b/app/controllers/confirmations_controller.rb @@ -3,6 +3,8 @@ class ConfirmationsController < Devise::ConfirmationsController include AcceptsPendingInvitations + feature_category :users + def almost_there flash[:notice] = nil render layout: "devise_empty" diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb index f82cde8e10a..23ffcd50369 100644 --- a/app/controllers/dashboard/groups_controller.rb +++ b/app/controllers/dashboard/groups_controller.rb @@ -5,6 +5,8 @@ class Dashboard::GroupsController < Dashboard::ApplicationController skip_cross_project_access_check :index + feature_category :subgroups + def index groups = GroupsFinder.new(current_user, all_available: false).execute render_group_tree(groups) diff --git a/app/controllers/dashboard/labels_controller.rb b/app/controllers/dashboard/labels_controller.rb index 89d87c2d5c8..b661efa12c0 100644 --- a/app/controllers/dashboard/labels_controller.rb +++ b/app/controllers/dashboard/labels_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Dashboard::LabelsController < Dashboard::ApplicationController + feature_category :issue_tracking + def index respond_to do |format| format.json { render json: LabelSerializer.new.represent_appearance(labels) } @@ -9,8 +11,8 @@ class Dashboard::LabelsController < Dashboard::ApplicationController def labels finder_params = { project_ids: projects.select(:id) } - labels = LabelsFinder.new(current_user, finder_params).execute - GlobalLabel.build_collection(labels) + LabelsFinder.new(current_user, finder_params).execute + .select('DISTINCT ON (labels.title) labels.*') end end diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb index 14f9a026688..e17b16c26a2 100644 --- a/app/controllers/dashboard/milestones_controller.rb +++ b/app/controllers/dashboard/milestones_controller.rb @@ -4,6 +4,8 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController before_action :projects before_action :groups, only: :index + feature_category :issue_tracking + def index respond_to do |format| format.html do diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index 2bd6fd85381..f7a74f40e4b 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -14,6 +14,8 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController before_action :projects, only: [:index] skip_cross_project_access_check :index, :starred + feature_category :projects + def index respond_to do |format| format.html do diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb index a8ca3dbd0e7..6fe3d878639 100644 --- a/app/controllers/dashboard/snippets_controller.rb +++ b/app/controllers/dashboard/snippets_controller.rb @@ -7,6 +7,8 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController skip_cross_project_access_check :index + feature_category :snippets + def index @snippet_counts = Snippets::CountService .new(current_user, author: current_user) diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb index 4fc2f7b0571..0ae326b5d94 100644 --- a/app/controllers/dashboard/todos_controller.rb +++ b/app/controllers/dashboard/todos_controller.rb @@ -9,6 +9,8 @@ class Dashboard::TodosController < Dashboard::ApplicationController before_action :authorize_read_group!, only: :index before_action :find_todos, only: [:index, :destroy_all] + feature_category :issue_tracking + def index @sort = params[:sort] @todos = @todos.page(params[:page]) diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 07cc31fb7d3..a88cf64d842 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -15,6 +15,10 @@ class DashboardController < Dashboard::ApplicationController respond_to :html + feature_category :audit_events, [:activity] + feature_category :issue_tracking, [:issues, :issues_calendar] + feature_category :code_review, [:merge_requests] + def activity respond_to do |format| format.html diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb index 67db797b80a..aa4196b1c18 100644 --- a/app/controllers/explore/groups_controller.rb +++ b/app/controllers/explore/groups_controller.rb @@ -3,6 +3,8 @@ class Explore::GroupsController < Explore::ApplicationController include GroupTree + feature_category :subgroups + def index render_group_tree GroupsFinder.new(current_user).execute end diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index b3fa089a712..42795e418a4 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -18,6 +18,8 @@ class Explore::ProjectsController < Explore::ApplicationController rescue_from PageOutOfBoundsError, with: :page_out_of_bounds + feature_category :projects + def index @projects = load_projects diff --git a/app/controllers/explore/snippets_controller.rb b/app/controllers/explore/snippets_controller.rb index 3a56a48e578..91ab18f2f55 100644 --- a/app/controllers/explore/snippets_controller.rb +++ b/app/controllers/explore/snippets_controller.rb @@ -3,6 +3,8 @@ class Explore::SnippetsController < Explore::ApplicationController include Gitlab::NoteableMetadata + feature_category :snippets + def index @snippets = SnippetsFinder.new(current_user, explore: true) .execute diff --git a/app/controllers/google_api/authorizations_controller.rb b/app/controllers/google_api/authorizations_controller.rb index 5723ccc14a7..76a1c43dfa3 100644 --- a/app/controllers/google_api/authorizations_controller.rb +++ b/app/controllers/google_api/authorizations_controller.rb @@ -6,6 +6,8 @@ module GoogleApi before_action :validate_session_key! + feature_category :kubernetes_management + def callback token, expires_at = GoogleApi::CloudPlatform::Client .new(nil, callback_google_api_auth_url) diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index 123102bf793..b5deed70380 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -26,6 +26,8 @@ class GraphqlController < ApplicationController # callback execution order here around_action :sessionless_bypass_admin_mode!, if: :sessionless_user? + feature_category :not_owned + def execute result = multiplex? ? execute_multiplex : execute_query @@ -46,6 +48,10 @@ class GraphqlController < ApplicationController render_error(exception.message, status: :unprocessable_entity) end + rescue_from ::GraphQL::CoercionError do |exception| + render_error(exception.message, status: :unprocessable_entity) + end + private def set_user_last_activity @@ -81,7 +87,7 @@ class GraphqlController < ApplicationController end def context - @context ||= { current_user: current_user, is_sessionless_user: !!sessionless_user? } + @context ||= { current_user: current_user, is_sessionless_user: !!sessionless_user?, request: request } end def build_variables(variable_info) @@ -113,6 +119,12 @@ class GraphqlController < ApplicationController # Merging to :metadata will ensure these are logged as top level keys payload[:metadata] ||= {} - payload[:metadata].merge!(graphql: { operation_name: params[:operationName] }) + payload[:metadata].merge!(graphql: logs) + end + + def logs + RequestStore.store[:graphql_logs].to_h + .except(:duration_s, :query_string) + .merge(operation_name: params[:operationName]) end end diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb index 8e4dc2bb6e9..1f13be449a9 100644 --- a/app/controllers/groups/avatars_controller.rb +++ b/app/controllers/groups/avatars_controller.rb @@ -5,6 +5,8 @@ class Groups::AvatarsController < Groups::ApplicationController skip_cross_project_access_check :destroy + feature_category :subgroups + def destroy @group.remove_avatar! @group.save diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb index ea7e83a2caf..b971c5783a8 100644 --- a/app/controllers/groups/boards_controller.rb +++ b/app/controllers/groups/boards_controller.rb @@ -12,6 +12,8 @@ class Groups::BoardsController < Groups::ApplicationController push_frontend_feature_flag(:boards_with_swimlanes, group, default_enabled: false) end + feature_category :boards + private def assign_endpoint_vars diff --git a/app/controllers/groups/children_controller.rb b/app/controllers/groups/children_controller.rb index 236a19a8dc4..718914dea35 100644 --- a/app/controllers/groups/children_controller.rb +++ b/app/controllers/groups/children_controller.rb @@ -5,6 +5,8 @@ module Groups before_action :group skip_cross_project_access_check :index + feature_category :subgroups + def index parent = if params[:parent_id].present? GroupFinder.new(current_user).execute(id: params[:parent_id]) diff --git a/app/controllers/groups/deploy_tokens_controller.rb b/app/controllers/groups/deploy_tokens_controller.rb index de951f2cb9f..79152bf2695 100644 --- a/app/controllers/groups/deploy_tokens_controller.rb +++ b/app/controllers/groups/deploy_tokens_controller.rb @@ -3,6 +3,8 @@ class Groups::DeployTokensController < Groups::ApplicationController before_action :authorize_destroy_deploy_token! + feature_category :continuous_delivery + def revoke @token = @group.deploy_tokens.find(params[:id]) @token.revoke! diff --git a/app/controllers/groups/group_links_controller.rb b/app/controllers/groups/group_links_controller.rb index c395b93f4e7..3b775af9722 100644 --- a/app/controllers/groups/group_links_controller.rb +++ b/app/controllers/groups/group_links_controller.rb @@ -4,6 +4,8 @@ class Groups::GroupLinksController < Groups::ApplicationController before_action :authorize_admin_group! before_action :group_link, only: [:update, :destroy] + feature_category :subgroups + def create shared_with_group = Group.find(params[:shared_with_group_id]) if params[:shared_with_group_id].present? @@ -24,6 +26,15 @@ class Groups::GroupLinksController < Groups::ApplicationController def update Groups::GroupLinks::UpdateService.new(@group_link).execute(group_link_params) + + if @group_link.expires? + render json: { + expires_in: helpers.distance_of_time_in_words_to_now(@group_link.expires_at), + expires_soon: @group_link.expires_soon? + } + else + render json: {} + end end def destroy diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 63311ab983b..5df7ff0632a 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -19,6 +19,8 @@ class Groups::GroupMembersController < Groups::ApplicationController :approve_access_request, :leave, :resend_invite, :override + feature_category :authentication_and_authorization + def index @sort = params[:sort].presence || sort_value_name @@ -69,6 +71,10 @@ class Groups::GroupMembersController < Groups::ApplicationController def filter_params params.permit(:two_factor, :search).merge(sort: @sort) end + + def membershipable_members + group.members + end end Groups::GroupMembersController.prepend_if_ee('EE::Groups::GroupMembersController') diff --git a/app/controllers/groups/imports_controller.rb b/app/controllers/groups/imports_controller.rb index b611685f9bc..7cf39e378db 100644 --- a/app/controllers/groups/imports_controller.rb +++ b/app/controllers/groups/imports_controller.rb @@ -3,6 +3,8 @@ class Groups::ImportsController < Groups::ApplicationController include ContinueParams + feature_category :importers + def show if @group.import_state.nil? || @group.import_state.finished? if continue_params[:to] diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb index 1034ca6cd7b..34856f8d84e 100644 --- a/app/controllers/groups/labels_controller.rb +++ b/app/controllers/groups/labels_controller.rb @@ -2,6 +2,7 @@ class Groups::LabelsController < Groups::ApplicationController include ToggleSubscriptionAction + include ShowInheritedLabelsChecker before_action :label, only: [:edit, :update, :destroy] before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update, :destroy] @@ -9,11 +10,14 @@ class Groups::LabelsController < Groups::ApplicationController respond_to :html + feature_category :issue_tracking + def index respond_to do |format| format.html do - @labels = GroupLabelsFinder - .new(current_user, @group, params.merge(sort: sort)).execute + # at group level we do not want to list project labels, + # we only want `only_group_labels = false` when pulling labels for label filter dropdowns, fetched through json + @labels = available_labels(params.merge(only_group_labels: true)).page(params[:page]) end format.json do render json: LabelSerializer.new.represent_appearance(available_labels) @@ -60,13 +64,7 @@ class Groups::LabelsController < Groups::ApplicationController def destroy @label.destroy - - respond_to do |format| - format.html do - redirect_to group_labels_path(@group), status: :found, notice: "#{@label.name} deleted permanently" - end - format.js - end + redirect_to group_labels_path(@group), status: :found, notice: "#{@label.name} deleted permanently" end protected @@ -80,7 +78,7 @@ class Groups::LabelsController < Groups::ApplicationController end def label - @label ||= @group.labels.find(params[:id]) + @label ||= available_labels(params.merge(only_group_labels: true)).find(params[:id]) end alias_method :subscribable_resource, :label @@ -108,15 +106,17 @@ class Groups::LabelsController < Groups::ApplicationController session[:previous_labels_path] = URI(request.referer || '').path end - def available_labels + def available_labels(options = params) @available_labels ||= LabelsFinder.new( current_user, group_id: @group.id, - only_group_labels: params[:only_group_labels], - include_ancestor_groups: params[:include_ancestor_groups], - include_descendant_groups: params[:include_descendant_groups], - search: params[:search]).execute + only_group_labels: options[:only_group_labels], + include_ancestor_groups: show_inherited_labels?(params[:include_ancestor_groups]), + sort: sort, + subscribed: options[:subscribed], + include_descendant_groups: options[:include_descendant_groups], + search: options[:search]).execute end def sort diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index df3fb6b67c2..173a24ceb74 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -3,12 +3,14 @@ class Groups::MilestonesController < Groups::ApplicationController include MilestoneActions - before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels, :destroy] + before_action :milestone, only: [:edit, :show, :update, :issues, :merge_requests, :participants, :labels, :destroy] before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update, :destroy] before_action do push_frontend_feature_flag(:burnup_charts, @group) end + feature_category :issue_tracking + def index respond_to do |format| format.html do diff --git a/app/controllers/groups/packages_controller.rb b/app/controllers/groups/packages_controller.rb index 600acc72e67..47f1816cc4c 100644 --- a/app/controllers/groups/packages_controller.rb +++ b/app/controllers/groups/packages_controller.rb @@ -4,6 +4,8 @@ module Groups class PackagesController < Groups::ApplicationController before_action :verify_packages_enabled! + feature_category :package_registry + private def verify_packages_enabled! diff --git a/app/controllers/groups/registry/repositories_controller.rb b/app/controllers/groups/registry/repositories_controller.rb index 14651e0794a..d914e0bffc6 100644 --- a/app/controllers/groups/registry/repositories_controller.rb +++ b/app/controllers/groups/registry/repositories_controller.rb @@ -2,9 +2,13 @@ module Groups module Registry class RepositoriesController < Groups::ApplicationController + include PackagesHelper + before_action :verify_container_registry_enabled! before_action :authorize_read_container_image! + feature_category :package_registry + def index respond_to do |format| format.html @@ -13,7 +17,7 @@ module Groups .execute .with_api_entity_associations - track_event(:list_repositories) + track_package_event(:list_repositories, :container) serializer = ContainerRepositoriesSerializer .new(current_user: current_user) diff --git a/app/controllers/groups/releases_controller.rb b/app/controllers/groups/releases_controller.rb index 500c57a6f3e..6a42f30b847 100644 --- a/app/controllers/groups/releases_controller.rb +++ b/app/controllers/groups/releases_controller.rb @@ -2,6 +2,8 @@ module Groups class ReleasesController < Groups::ApplicationController + feature_category :release_evidence + def index respond_to do |format| format.json do diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb index edebffe2912..dbfd31ebcad 100644 --- a/app/controllers/groups/runners_controller.rb +++ b/app/controllers/groups/runners_controller.rb @@ -7,6 +7,8 @@ class Groups::RunnersController < Groups::ApplicationController before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show] + feature_category :continuous_integration + def show render 'shared/runners/show' end diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb index bf3a38ce57b..0c72c8a037b 100644 --- a/app/controllers/groups/settings/ci_cd_controller.rb +++ b/app/controllers/groups/settings/ci_cd_controller.rb @@ -3,6 +3,8 @@ module Groups module Settings class CiCdController < Groups::ApplicationController + include RunnerSetupScripts + skip_cross_project_access_check :show before_action :authorize_admin_group! before_action :authorize_update_max_artifacts_size!, only: [:update] @@ -11,6 +13,8 @@ module Groups end before_action :define_variables, only: [:show] + feature_category :continuous_integration + NUMBER_OF_RUNNERS_PER_PAGE = 4 def show @@ -49,6 +53,10 @@ module Groups redirect_to group_settings_ci_cd_path end + def runner_setup_scripts + private_runner_setup_scripts(group: group) + end + private def define_variables diff --git a/app/controllers/groups/settings/integrations_controller.rb b/app/controllers/groups/settings/integrations_controller.rb index e8551a7f270..b089cfdf341 100644 --- a/app/controllers/groups/settings/integrations_controller.rb +++ b/app/controllers/groups/settings/integrations_controller.rb @@ -7,6 +7,8 @@ module Groups before_action :authorize_admin_group! + feature_category :integrations + def index @integrations = Service.find_or_initialize_all(Service.for_group(group)).sort_by(&:title) end diff --git a/app/controllers/groups/settings/repository_controller.rb b/app/controllers/groups/settings/repository_controller.rb index e2fbdc39692..ccc1fa12458 100644 --- a/app/controllers/groups/settings/repository_controller.rb +++ b/app/controllers/groups/settings/repository_controller.rb @@ -10,6 +10,8 @@ module Groups push_frontend_feature_flag(:ajax_new_deploy_token, @group) end + feature_category :continuous_delivery + def create_deploy_token result = Groups::DeployTokens::CreateService.new(@group, current_user, deploy_token_params).execute @new_deploy_token = result[:deploy_token] diff --git a/app/controllers/groups/shared_projects_controller.rb b/app/controllers/groups/shared_projects_controller.rb index 30b7bfc70ae..90ec64d4768 100644 --- a/app/controllers/groups/shared_projects_controller.rb +++ b/app/controllers/groups/shared_projects_controller.rb @@ -6,6 +6,8 @@ module Groups before_action :group skip_cross_project_access_check :index + feature_category :subgroups + def index shared_projects = GroupProjectsFinder.new( group: group, diff --git a/app/controllers/groups/uploads_controller.rb b/app/controllers/groups/uploads_controller.rb index 3ae7e36c740..49249f87d31 100644 --- a/app/controllers/groups/uploads_controller.rb +++ b/app/controllers/groups/uploads_controller.rb @@ -9,6 +9,8 @@ class Groups::UploadsController < Groups::ApplicationController before_action :authorize_upload_file!, only: [:create, :authorize] before_action :verify_workhorse_api!, only: [:authorize] + feature_category :subgroups + private def upload_model_class diff --git a/app/controllers/groups/variables_controller.rb b/app/controllers/groups/variables_controller.rb index fb639f6e472..51670325ce3 100644 --- a/app/controllers/groups/variables_controller.rb +++ b/app/controllers/groups/variables_controller.rb @@ -6,6 +6,8 @@ module Groups skip_cross_project_access_check :show, :update + feature_category :continuous_integration + def show respond_to do |format| format.json do diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 2d6f5d0377a..6f8dc75f6bd 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -30,6 +30,7 @@ class GroupsController < Groups::ApplicationController before_action do push_frontend_feature_flag(:vue_issuables_list, @group) + push_frontend_feature_flag(:deployment_filters) end before_action do @@ -46,6 +47,17 @@ class GroupsController < Groups::ApplicationController layout :determine_layout + feature_category :subgroups, [ + :index, :new, :create, :show, :edit, :update, + :destroy, :details, :transfer + ] + + feature_category :audit_events, [:activity] + feature_category :issue_tracking, [:issues, :issues_calendar, :preview_markdown] + feature_category :code_review, [:merge_requests, :unfoldered_environment_names] + feature_category :projects, [:projects] + feature_category :importers, [:export, :download_export] + def index redirect_to(current_user ? dashboard_groups_path : explore_groups_path) end @@ -168,6 +180,16 @@ class GroupsController < Groups::ApplicationController end end + def unfoldered_environment_names + return render_404 unless Feature.enabled?(:deployment_filters) + + respond_to do |format| + format.json do + render json: EnvironmentNamesFinder.new(@group, current_user).execute + end + end + end + protected def render_show_html @@ -230,7 +252,9 @@ class GroupsController < Groups::ApplicationController :two_factor_grace_period, :project_creation_level, :subgroup_creation_level, - :default_branch_protection + :default_branch_protection, + :default_branch_name, + :allow_mfa_for_subgroups ] end diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb index a1bbcf34f69..5a5200452de 100644 --- a/app/controllers/help_controller.rb +++ b/app/controllers/help_controller.rb @@ -2,6 +2,7 @@ class HelpController < ApplicationController skip_before_action :authenticate_user!, unless: :public_visibility_restricted? + feature_category :not_owned layout 'help' @@ -26,17 +27,10 @@ class HelpController < ApplicationController respond_to do |format| format.any(:markdown, :md, :html) do - # Note: We are purposefully NOT using `Rails.root.join` because of https://gitlab.com/gitlab-org/gitlab/-/issues/216028. - path = File.join(Rails.root, 'doc', "#{@path}.md") - - if File.exist?(path) - # Remove YAML frontmatter so that it doesn't look weird - @markdown = File.read(path).gsub(YAML_FRONT_MATTER_REGEXP, '') - - render 'show.html.haml' + if redirect_to_documentation_website? + redirect_to documentation_url else - # Force template to Haml - render 'errors/not_found.html.haml', layout: 'errors', status: :not_found + render_documentation end end @@ -75,4 +69,47 @@ class HelpController < ApplicationController params end + + def redirect_to_documentation_website? + return false unless Feature.enabled?(:help_page_documentation_redirect) + return false unless Gitlab::UrlSanitizer.valid_web?(documentation_url) + + true + end + + def documentation_url + return unless documentation_base_url + + @documentation_url ||= Gitlab::Utils.append_path(documentation_base_url, documentation_file_path) + end + + def documentation_base_url + @documentation_base_url ||= Gitlab::CurrentSettings.current_application_settings.help_page_documentation_base_url.presence + end + + def documentation_file_path + @documentation_file_path ||= [version_segment, 'ee', "#{@path}.html"].compact.join('/') + end + + def version_segment + return if Gitlab.pre_release? + + version = Gitlab.version_info + [version.major, version.minor].join('.') + end + + def render_documentation + # Note: We are purposefully NOT using `Rails.root.join` because of https://gitlab.com/gitlab-org/gitlab/-/issues/216028. + path = File.join(Rails.root, 'doc', "#{@path}.md") + + if File.exist?(path) + # Remove YAML frontmatter so that it doesn't look weird + @markdown = File.read(path).gsub(YAML_FRONT_MATTER_REGEXP, '') + + render 'show.html.haml' + else + # Force template to Haml + render 'errors/not_found.html.haml', layout: 'errors', status: :not_found + end + end end diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb index 2c17f5b5542..8c0414ad5da 100644 --- a/app/controllers/ide_controller.rb +++ b/app/controllers/ide_controller.rb @@ -11,6 +11,8 @@ class IdeController < ApplicationController push_frontend_feature_flag(:schema_linting) end + feature_category :web_ide + def index Gitlab::UsageDataCounters::WebIdeCounter.increment_views_count end diff --git a/app/controllers/import/available_namespaces_controller.rb b/app/controllers/import/available_namespaces_controller.rb index 7983b4f20b5..c6211b33d28 100644 --- a/app/controllers/import/available_namespaces_controller.rb +++ b/app/controllers/import/available_namespaces_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Import::AvailableNamespacesController < ApplicationController + feature_category :importers + def index render json: NamespaceSerializer.new.represent(current_user.manageable_groups_with_routes) end diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb index 8a7a4c92b37..151ba46e629 100644 --- a/app/controllers/import/base_controller.rb +++ b/app/controllers/import/base_controller.rb @@ -4,6 +4,7 @@ class Import::BaseController < ApplicationController include ActionView::Helpers::SanitizeHelper before_action :import_rate_limit, only: [:create] + feature_category :importers def status respond_to do |format| diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb new file mode 100644 index 00000000000..cb2922c2d47 --- /dev/null +++ b/app/controllers/import/bulk_imports_controller.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +class Import::BulkImportsController < ApplicationController + before_action :ensure_group_import_enabled + before_action :verify_blocked_uri, only: :status + + feature_category :importers + + rescue_from Gitlab::BulkImport::Client::ConnectionError, with: :bulk_import_connection_error + + def configure + session[access_token_key] = params[access_token_key]&.strip + session[url_key] = params[url_key] + + redirect_to status_import_bulk_import_url + end + + def status + respond_to do |format| + format.json do + render json: { importable_data: serialized_importable_data } + end + + format.html + end + end + + private + + def serialized_importable_data + serializer.represent(importable_data, {}, Import::BulkImportEntity) + end + + def serializer + @serializer ||= BaseSerializer.new(current_user: current_user) + end + + def importable_data + client.get('groups', top_level_only: true) + end + + def client + @client ||= Gitlab::BulkImport::Client.new( + uri: session[url_key], + token: session[access_token_key] + ) + end + + def import_params + params.permit(access_token_key, url_key) + end + + def ensure_group_import_enabled + render_404 unless Feature.enabled?(:bulk_import) + end + + def access_token_key + :bulk_import_gitlab_access_token + end + + def url_key + :bulk_import_gitlab_url + end + + def verify_blocked_uri + Gitlab::UrlBlocker.validate!( + session[url_key], + **{ + allow_localhost: allow_local_requests?, + allow_local_network: allow_local_requests?, + schemes: %w(http https) + } + ) + rescue Gitlab::UrlBlocker::BlockedUrlError => e + clear_session_data + + redirect_to new_group_path, alert: _('Specified URL cannot be used: "%{reason}"') % { reason: e.message } + end + + def allow_local_requests? + Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services? + end + + def bulk_import_connection_error(error) + clear_session_data + + error_message = _("Unable to connect to server: %{error}") % { error: error } + flash[:alert] = error_message + + respond_to do |format| + format.json do + render json: { + error: { + message: error_message, + redirect: new_group_path + } + }, status: :unprocessable_entity + end + format.html do + redirect_to new_group_path + end + end + end + + def clear_session_data + session[url_key] = nil + session[access_token_key] = nil + end +end diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb index a34bc9c953f..bcbf5938e11 100644 --- a/app/controllers/import/fogbugz_controller.rb +++ b/app/controllers/import/fogbugz_controller.rb @@ -136,7 +136,7 @@ class Import::FogbugzController < Import::BaseController def verify_blocked_uri Gitlab::UrlBlocker.validate!( params[:uri], - { + **{ allow_localhost: allow_local_requests?, allow_local_network: allow_local_requests?, schemes: %w(http https) diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index 29fe34f0734..a1adc6e062a 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -108,7 +108,7 @@ class Import::GithubController < Import::BaseController @client ||= if Feature.enabled?(:remove_legacy_github_client) Gitlab::GithubImport::Client.new(session[access_token_key]) else - Gitlab::LegacyGithubImport::Client.new(session[access_token_key], client_options) + Gitlab::LegacyGithubImport::Client.new(session[access_token_key], **client_options) end end diff --git a/app/controllers/import/gitlab_groups_controller.rb b/app/controllers/import/gitlab_groups_controller.rb index 330af68385e..d8118477a80 100644 --- a/app/controllers/import/gitlab_groups_controller.rb +++ b/app/controllers/import/gitlab_groups_controller.rb @@ -6,6 +6,8 @@ class Import::GitlabGroupsController < ApplicationController before_action :ensure_group_import_enabled before_action :import_rate_limit, only: %i[create] + feature_category :importers + def create unless file_is_valid?(group_params[:file]) return redirect_back_or_default(options: { alert: s_('GroupImport|Unable to process group import file') }) diff --git a/app/controllers/import/manifest_controller.rb b/app/controllers/import/manifest_controller.rb index 9c47e6d4b0b..fdca6da95c5 100644 --- a/app/controllers/import/manifest_controller.rb +++ b/app/controllers/import/manifest_controller.rb @@ -26,8 +26,7 @@ class Import::ManifestController < Import::BaseController manifest = Gitlab::ManifestImport::Manifest.new(params[:manifest].tempfile) if manifest.valid? - session[:manifest_import_repositories] = manifest.projects - session[:manifest_import_group_id] = group.id + manifest_import_metadata.save(manifest.projects, group.id) redirect_to status_import_manifest_path else @@ -96,12 +95,16 @@ class Import::ManifestController < Import::BaseController # rubocop: disable CodeReuse/ActiveRecord def group - @group ||= Group.find_by(id: session[:manifest_import_group_id]) + @group ||= Group.find_by(id: manifest_import_metadata.group_id) end # rubocop: enable CodeReuse/ActiveRecord + def manifest_import_metadata + @manifest_import_status ||= Gitlab::ManifestImport::Metadata.new(current_user, fallback: session) + end + def repositories - @repositories ||= session[:manifest_import_repositories] + @repositories ||= manifest_import_metadata.repositories end def find_jobs diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index aa9c7d01ba3..c7b8486d1c9 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -4,6 +4,7 @@ class InvitesController < ApplicationController include Gitlab::Utils::StrongMemoize before_action :member + before_action :ensure_member_exists before_action :invite_details skip_before_action :authenticate_user!, only: :decline @@ -11,14 +12,17 @@ class InvitesController < ApplicationController respond_to :html + feature_category :authentication_and_authorization + def show - track_experiment('opened') + track_new_user_invite_experiment('opened') accept if skip_invitation_prompt? end def accept if member.accept_invite!(current_user) - track_experiment('accepted') + track_new_user_invite_experiment('accepted') + track_invitation_reminders_experiment('accepted') redirect_to invite_details[:path], notice: _("You have been granted %{member_human_access} access to %{title} %{name}.") % { member_human_access: member.human_access, title: invite_details[:title], name: invite_details[:name] } else @@ -28,6 +32,8 @@ class InvitesController < ApplicationController def decline if member.decline_invite! + return render layout: 'devise_experimental_onboarding_issues' if !current_user && member.invite_to_unknown_user? && member.created_by + path = if current_user dashboard_projects_path @@ -59,14 +65,16 @@ class InvitesController < ApplicationController end def member - return @member if defined?(@member) - - @token = params[:id] - @member = Member.find_by_invite_token(@token) + strong_memoize(:member) do + @token = params[:id] + Member.find_by_invite_token(@token) + end + end - return render_404 unless @member + def ensure_member_exists + return if member - @member + render_404 end def authenticate_user! @@ -76,10 +84,7 @@ class InvitesController < ApplicationController notice << "or create an account" if Gitlab::CurrentSettings.allow_signup? notice = notice.join(' ') + "." - # this is temporary finder instead of using member method due to render_404 possibility - # will be resolved via https://gitlab.com/gitlab-org/gitlab/-/issues/245325 - initial_member = Member.find_by_invite_token(params[:id]) - redirect_params = initial_member ? { invite_email: initial_member.invite_email } : {} + redirect_params = member ? { invite_email: member.invite_email } : {} store_location_for :user, request.fullpath @@ -87,31 +92,43 @@ class InvitesController < ApplicationController end def invite_details - @invite_details ||= case @member.source + @invite_details ||= case member.source when Project { - name: @member.source.full_name, - url: project_url(@member.source), + name: member.source.full_name, + url: project_url(member.source), title: _("project"), - path: project_path(@member.source) + path: project_path(member.source) } when Group { - name: @member.source.name, - url: group_url(@member.source), + name: member.source.name, + url: group_url(member.source), title: _("group"), - path: group_path(@member.source) + path: group_path(member.source) } end end - def track_experiment(action) + def track_new_user_invite_experiment(action) return unless params[:new_user_invite] property = params[:new_user_invite] == 'experiment' ? 'experiment_group' : 'control_group' + track_experiment(:invite_email, action, property) + end + + def track_invitation_reminders_experiment(action) + return unless Gitlab::Experimentation.enabled?(:invitation_reminders) + + property = Gitlab::Experimentation.enabled_for_attribute?(:invitation_reminders, member.invite_email) ? 'experimental_group' : 'control_group' + + track_experiment(:invitation_reminders, action, property) + end + + def track_experiment(experiment_key, action, property) Gitlab::Tracking.event( - Gitlab::Experimentation::EXPERIMENTS[:invite_email][:tracking_category], + Gitlab::Experimentation.experiment(experiment_key).tracking_category, action, property: property, label: Digest::MD5.hexdigest(member.to_global_id.to_s) diff --git a/app/controllers/jira_connect/application_controller.rb b/app/controllers/jira_connect/application_controller.rb index a84f25998a6..9c311f92b69 100644 --- a/app/controllers/jira_connect/application_controller.rb +++ b/app/controllers/jira_connect/application_controller.rb @@ -7,6 +7,8 @@ class JiraConnect::ApplicationController < ApplicationController skip_before_action :verify_authenticity_token before_action :verify_atlassian_jwt! + feature_category :integrations + attr_reader :current_jira_installation private diff --git a/app/controllers/jira_connect/events_controller.rb b/app/controllers/jira_connect/events_controller.rb index 8f79c82d847..d833491b8f7 100644 --- a/app/controllers/jira_connect/events_controller.rb +++ b/app/controllers/jira_connect/events_controller.rb @@ -1,10 +1,14 @@ # frozen_string_literal: true class JiraConnect::EventsController < JiraConnect::ApplicationController + # See https://developer.atlassian.com/cloud/jira/software/app-descriptor/#lifecycle + skip_before_action :verify_atlassian_jwt!, only: :installed before_action :verify_qsh_claim!, only: :uninstalled def installed + return head :ok if atlassian_jwt_valid? + installation = JiraConnectInstallation.new(install_params) if installation.save diff --git a/app/controllers/jira_connect/users_controller.rb b/app/controllers/jira_connect/users_controller.rb new file mode 100644 index 00000000000..571d9f87779 --- /dev/null +++ b/app/controllers/jira_connect/users_controller.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class JiraConnect::UsersController < ApplicationController + feature_category :integrations + + layout 'devise_experimental_onboarding_issues' + + def show + @jira_app_link = params.delete(:return_to) + end +end diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb index 3e7755046cd..5199bb25c8c 100644 --- a/app/controllers/jwt_controller.rb +++ b/app/controllers/jwt_controller.rb @@ -8,6 +8,8 @@ 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 + SERVICES = { Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService }.freeze diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb index e5d4a4bb073..7d8c035c852 100644 --- a/app/controllers/notification_settings_controller.rb +++ b/app/controllers/notification_settings_controller.rb @@ -3,6 +3,8 @@ class NotificationSettingsController < ApplicationController before_action :authenticate_user! + feature_category :users + def create return render_404 unless can_read?(resource) diff --git a/app/controllers/oauth/jira/authorizations_controller.rb b/app/controllers/oauth/jira/authorizations_controller.rb index f552b0dc10c..f23149c8544 100644 --- a/app/controllers/oauth/jira/authorizations_controller.rb +++ b/app/controllers/oauth/jira/authorizations_controller.rb @@ -8,6 +8,8 @@ class Oauth::Jira::AuthorizationsController < ApplicationController skip_before_action :authenticate_user! skip_before_action :verify_authenticity_token + feature_category :integrations + # 1. Rewire Jira OAuth initial request to our stablished OAuth authorization URL. def new session[:redirect_uri] = params['redirect_uri'] diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index b798d6680bc..c9791703413 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true class OmniauthCallbacksController < Devise::OmniauthCallbacksController - include AuthenticatesWithTwoFactor - include Authenticates2FAForAdminMode + include AuthenticatesWithTwoFactorForAdminMode include Devise::Controllers::Rememberable include AuthHelper include InitializesCurrentUserMode @@ -12,6 +11,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController protect_from_forgery except: [:kerberos, :saml, :cas3, :failure], with: :exception, prepend: true + feature_category :authentication_and_authorization + def handle_omniauth omniauth_flow(Gitlab::Auth::OAuth) end diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index c27226c3f3f..bc6975f8953 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -7,6 +7,8 @@ class PasswordsController < Devise::PasswordsController before_action :check_password_authentication_available, only: [:create] before_action :throttle_reset, only: [:create] + feature_category :authentication_and_authorization + # rubocop: disable CodeReuse/ActiveRecord def edit super diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb index b19285e98bb..d8419be9f23 100644 --- a/app/controllers/profiles/accounts_controller.rb +++ b/app/controllers/profiles/accounts_controller.rb @@ -3,6 +3,8 @@ class Profiles::AccountsController < Profiles::ApplicationController include AuthHelper + feature_category :users + def show render(locals: show_view_variables) end diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb index e4cd5d65e1a..1233c906406 100644 --- a/app/controllers/profiles/active_sessions_controller.rb +++ b/app/controllers/profiles/active_sessions_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Profiles::ActiveSessionsController < Profiles::ApplicationController + feature_category :users + def index @sessions = ActiveSession.list(current_user).reject(&:is_impersonated) end diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb index 3378a09628c..d9e4b9a149d 100644 --- a/app/controllers/profiles/avatars_controller.rb +++ b/app/controllers/profiles/avatars_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Profiles::AvatarsController < Profiles::ApplicationController + feature_category :users + def destroy @user = current_user diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb index 80b8279e91e..8cfec247b7a 100644 --- a/app/controllers/profiles/chat_names_controller.rb +++ b/app/controllers/profiles/chat_names_controller.rb @@ -4,6 +4,8 @@ class Profiles::ChatNamesController < Profiles::ApplicationController before_action :chat_name_token, only: [:new] before_action :chat_name_params, only: [:new, :create, :deny] + feature_category :users + def index @chat_names = current_user.chat_names end diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb index da553e34ef6..6e5b18cb885 100644 --- a/app/controllers/profiles/emails_controller.rb +++ b/app/controllers/profiles/emails_controller.rb @@ -5,6 +5,8 @@ class Profiles::EmailsController < Profiles::ApplicationController before_action -> { rate_limit!(:profile_add_new_email) }, only: [:create] before_action -> { rate_limit!(:profile_resend_email_confirmation) }, only: [:resend_confirmation_instructions] + feature_category :users + def index @primary_email = current_user.email @emails = current_user.emails.order_id_desc diff --git a/app/controllers/profiles/gpg_keys_controller.rb b/app/controllers/profiles/gpg_keys_controller.rb index 8c34a66c374..7f04927f517 100644 --- a/app/controllers/profiles/gpg_keys_controller.rb +++ b/app/controllers/profiles/gpg_keys_controller.rb @@ -3,6 +3,8 @@ class Profiles::GpgKeysController < Profiles::ApplicationController before_action :set_gpg_key, only: [:destroy, :revoke] + feature_category :users + def index @gpg_keys = current_user.gpg_keys.with_subkeys @gpg_key = GpgKey.new diff --git a/app/controllers/profiles/groups_controller.rb b/app/controllers/profiles/groups_controller.rb index 04b5ee270dc..e76ee0a6cea 100644 --- a/app/controllers/profiles/groups_controller.rb +++ b/app/controllers/profiles/groups_controller.rb @@ -3,6 +3,8 @@ class Profiles::GroupsController < Profiles::ApplicationController include RoutableActions + feature_category :users + def update group = find_routable!(Group, params[:id]) notification_setting = current_user.notification_settings_for(group) diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb index 965493955ac..1e6340f285e 100644 --- a/app/controllers/profiles/keys_controller.rb +++ b/app/controllers/profiles/keys_controller.rb @@ -3,6 +3,8 @@ class Profiles::KeysController < Profiles::ApplicationController skip_before_action :authenticate_user!, only: [:get_keys] + feature_category :users + def index @keys = current_user.keys.order_id_desc @key = Key.new diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb index bc51830c119..a3e7638cdbc 100644 --- a/app/controllers/profiles/notifications_controller.rb +++ b/app/controllers/profiles/notifications_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Profiles::NotificationsController < Profiles::ApplicationController + feature_category :users + # rubocop: disable CodeReuse/ActiveRecord def show @user = current_user diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb index fccbc29f598..85e901eb3eb 100644 --- a/app/controllers/profiles/passwords_controller.rb +++ b/app/controllers/profiles/passwords_controller.rb @@ -9,6 +9,8 @@ class Profiles::PasswordsController < Profiles::ApplicationController layout :determine_layout + feature_category :authentication_and_authorization + def new end diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb index 21adc032940..b005347c43a 100644 --- a/app/controllers/profiles/personal_access_tokens_controller.rb +++ b/app/controllers/profiles/personal_access_tokens_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Profiles::PersonalAccessTokensController < Profiles::ApplicationController + feature_category :authentication_and_authorization + def index set_index_vars @personal_access_token = finder.build diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb index ea4d3e861be..4d88491e9a8 100644 --- a/app/controllers/profiles/preferences_controller.rb +++ b/app/controllers/profiles/preferences_controller.rb @@ -3,6 +3,8 @@ class Profiles::PreferencesController < Profiles::ApplicationController before_action :user + feature_category :users + def show end diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index 5de6d84fdd9..e2f8baa8226 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -6,6 +6,8 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController push_frontend_feature_flag(:webauthn) end + feature_category :authentication_and_authorization + def show unless current_user.two_factor_enabled? current_user.otp_secret = User.generate_otp_secret(32) @@ -45,7 +47,10 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController end def create - if current_user.validate_and_consume_otp!(params[:pin_code]) + otp_validation_result = + ::Users::ValidateOtpService.new(current_user).execute(params[:pin_code]) + + if otp_validation_result[:status] == :success ActiveSession.destroy_all_but_current(current_user, session) Users::UpdateService.new(current_user, user: current_user, otp_required_for_login: true).execute! do |user| diff --git a/app/controllers/profiles/u2f_registrations_controller.rb b/app/controllers/profiles/u2f_registrations_controller.rb index 84ce4a56e64..32ca303e722 100644 --- a/app/controllers/profiles/u2f_registrations_controller.rb +++ b/app/controllers/profiles/u2f_registrations_controller.rb @@ -1,6 +1,8 @@ # 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 diff --git a/app/controllers/profiles/webauthn_registrations_controller.rb b/app/controllers/profiles/webauthn_registrations_controller.rb index 81b1dd6f710..a4a6d84f1ae 100644 --- a/app/controllers/profiles/webauthn_registrations_controller.rb +++ b/app/controllers/profiles/webauthn_registrations_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Profiles::WebauthnRegistrationsController < Profiles::ApplicationController + feature_category :authentication_and_authorization + def destroy webauthn_registration = current_user.webauthn_registrations.find(params[:id]) webauthn_registration.destroy diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 248d5755d92..c85c83688a4 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -2,6 +2,7 @@ class ProfilesController < Profiles::ApplicationController include ActionView::Helpers::SanitizeHelper + include Gitlab::Tracking before_action :user before_action :authorize_change_username!, only: :update_username @@ -10,6 +11,8 @@ class ProfilesController < Profiles::ApplicationController push_frontend_feature_flag(:webauthn) end + feature_category :users + def show end @@ -63,6 +66,8 @@ class ProfilesController < Profiles::ApplicationController @events = AuditEvent.where(entity_type: "User", entity_id: current_user.id) .order("created_at DESC") .page(params[:page]) + + Gitlab::Tracking.event(self.class.name, 'search_audit_event') end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/controllers/projects/alert_management_controller.rb b/app/controllers/projects/alert_management_controller.rb index 054dc8e6a35..0d0ef9b05cb 100644 --- a/app/controllers/projects/alert_management_controller.rb +++ b/app/controllers/projects/alert_management_controller.rb @@ -3,10 +3,13 @@ class Projects::AlertManagementController < Projects::ApplicationController before_action :authorize_read_alert_management_alert! + feature_category :alert_management + def index end def details @alert_id = params[:id] + push_frontend_feature_flag(:expose_environment_path_in_alert_details, @project) end end diff --git a/app/controllers/projects/alerting/notifications_controller.rb b/app/controllers/projects/alerting/notifications_controller.rb index fef8235628d..2241ded2db8 100644 --- a/app/controllers/projects/alerting/notifications_controller.rb +++ b/app/controllers/projects/alerting/notifications_controller.rb @@ -10,6 +10,8 @@ module Projects prepend_before_action :repository, :project_without_auth + feature_category :alert_management + def create token = extract_alert_manager_token(request) result = notify_service.execute(token) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 652687932fd..f6a92b07295 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -15,6 +15,8 @@ class Projects::ArtifactsController < Projects::ApplicationController MAX_PER_PAGE = 20 + feature_category :continuous_integration + def index # Loading artifacts is very expensive in projects with a lot of artifacts. # This feature flag prevents a DOS attack vector. diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb index 605d70d440b..e9c533daa80 100644 --- a/app/controllers/projects/autocomplete_sources_controller.rb +++ b/app/controllers/projects/autocomplete_sources_controller.rb @@ -3,6 +3,11 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController before_action :authorize_read_milestone!, only: :milestones + feature_category :issue_tracking, [:issues, :labels, :milestones, :commands] + feature_category :code_review, [:merge_requests] + feature_category :users, [:members] + feature_category :snippets, [:snippets] + def members render json: ::Projects::ParticipantsService.new(@project, current_user).execute(target) end diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb index 6e6bf09a32a..f228206032d 100644 --- a/app/controllers/projects/avatars_controller.rb +++ b/app/controllers/projects/avatars_controller.rb @@ -5,6 +5,8 @@ class Projects::AvatarsController < Projects::ApplicationController before_action :authorize_admin_project!, only: [:destroy] + feature_category :projects + def show @blob = @repository.blob_at_branch(@repository.root_ref, @project.avatar_in_git) diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb index eb47fec2b7e..855965ca6e1 100644 --- a/app/controllers/projects/badges_controller.rb +++ b/app/controllers/projects/badges_controller.rb @@ -6,6 +6,8 @@ class Projects::BadgesController < Projects::ApplicationController before_action :no_cache_headers, only: [:pipeline, :coverage] before_action :authorize_read_build!, only: [:pipeline, :coverage] + feature_category :continuous_integration + def pipeline pipeline_status = Gitlab::Badge::Pipeline::Status .new(project, params[:ref], opts: { diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index 374b4921dbc..d5de0d38152 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -9,6 +9,8 @@ class Projects::BlameController < Projects::ApplicationController before_action :assign_ref_vars before_action :authorize_download_code! + feature_category :source_code_management + def show @blob = @repository.blob_at(@commit.id, @path) diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 1568d9966dd..c6251d27b05 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -33,13 +33,14 @@ class Projects::BlobController < Projects::ApplicationController before_action :set_last_commit_sha, only: [:edit, :update] before_action only: :show do - push_frontend_feature_flag(:code_navigation, @project, default_enabled: true) - push_frontend_feature_flag(:suggest_pipeline) if experiment_enabled?(:suggest_pipeline) + push_frontend_experiment(:suggest_pipeline) push_frontend_feature_flag(:gitlab_ci_yml_preview, @project, default_enabled: false) end track_redis_hll_event :create, :update, name: 'g_edit_by_sfe', feature: :track_editor_edit_actions, feature_default_enabled: true + feature_category :source_code_management + def new commit unless @repository.empty? end diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb index 5ed35094476..193352ffa70 100644 --- a/app/controllers/projects/boards_controller.rb +++ b/app/controllers/projects/boards_controller.rb @@ -12,6 +12,8 @@ class Projects::BoardsController < Projects::ApplicationController push_frontend_feature_flag(:boards_with_swimlanes, project, default_enabled: false) end + feature_category :boards + private def assign_endpoint_vars diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index 7cfb4a508da..9124728ee25 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -13,6 +13,8 @@ class Projects::BranchesController < Projects::ApplicationController before_action :redirect_for_legacy_index_sort_or_search, only: [:index] before_action :limit_diverging_commit_counts!, only: [:diverging_commit_counts] + feature_category :source_code_management + def index respond_to do |format| format.html do diff --git a/app/controllers/projects/build_artifacts_controller.rb b/app/controllers/projects/build_artifacts_controller.rb index 99f4524eec5..148080a71f4 100644 --- a/app/controllers/projects/build_artifacts_controller.rb +++ b/app/controllers/projects/build_artifacts_controller.rb @@ -8,6 +8,8 @@ class Projects::BuildArtifactsController < Projects::ApplicationController before_action :extract_ref_name_and_path before_action :validate_artifacts!, except: [:download] + feature_category :continuous_integration + def download redirect_to download_project_job_artifacts_path(project, job, params: request.query_parameters) end diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index 6b3d70cb720..c5f6ed1c105 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -3,6 +3,8 @@ class Projects::BuildsController < Projects::ApplicationController before_action :authorize_read_build! + feature_category :continuous_integration + def index redirect_to project_jobs_path(project) end diff --git a/app/controllers/projects/ci/daily_build_group_report_results_controller.rb b/app/controllers/projects/ci/daily_build_group_report_results_controller.rb index b36c5f1aea6..d05ab1b4977 100644 --- a/app/controllers/projects/ci/daily_build_group_report_results_controller.rb +++ b/app/controllers/projects/ci/daily_build_group_report_results_controller.rb @@ -6,10 +6,11 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati MAX_ITEMS = 1000 REPORT_WINDOW = 90.days - before_action :validate_feature_flag! before_action :authorize_read_build_report_results! before_action :validate_param_type! + feature_category :continuous_integration + def index respond_to do |format| format.csv { send_data(render_csv(report_results), type: 'text/csv; charset=utf-8') } @@ -19,10 +20,6 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati private - def validate_feature_flag! - render_404 unless Feature.enabled?(:ci_download_daily_code_coverage, project, default_enabled: true) - end - def validate_param_type! respond_422 unless allowed_param_types.include?(param_type) end @@ -43,7 +40,7 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati end def report_results - Ci::DailyBuildGroupReportResultsFinder.new(finder_params).execute + Ci::DailyBuildGroupReportResultsFinder.new(**finder_params).execute end def finder_params diff --git a/app/controllers/projects/ci/lints_controller.rb b/app/controllers/projects/ci/lints_controller.rb index 813a0a9ddd5..7e900fc6051 100644 --- a/app/controllers/projects/ci/lints_controller.rb +++ b/app/controllers/projects/ci/lints_controller.rb @@ -6,6 +6,8 @@ class Projects::Ci::LintsController < Projects::ApplicationController push_frontend_feature_flag(:ci_lint_vue, project) end + feature_category :pipeline_authoring + def show end diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index b0c6f3cc6a1..2e48f2f0e45 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -21,6 +21,8 @@ class Projects::CommitController < Projects::ApplicationController BRANCH_SEARCH_LIMIT = 1000 + feature_category :source_code_management + def show apply_diff_view_cookie! diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index b161e44660e..d267ab732f9 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -15,6 +15,8 @@ class Projects::CommitsController < Projects::ApplicationController before_action :validate_ref!, except: :commits_root before_action :set_commits, except: :commits_root + feature_category :source_code_management + def commits_root redirect_to project_commits_path(@project, @project.default_branch) end diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index 943277afe95..6be0b465402 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -19,6 +19,8 @@ class Projects::CompareController < Projects::ApplicationController # Validation before_action :validate_refs! + feature_category :source_code_management + def index end diff --git a/app/controllers/projects/confluences_controller.rb b/app/controllers/projects/confluences_controller.rb index d563b34a362..fccbdf0bf91 100644 --- a/app/controllers/projects/confluences_controller.rb +++ b/app/controllers/projects/confluences_controller.rb @@ -3,6 +3,8 @@ class Projects::ConfluencesController < Projects::ApplicationController before_action :ensure_confluence + feature_category :integrations + def show end diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index c69bf029c73..3a5dd23047c 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -11,6 +11,8 @@ module Projects before_action :authorize_read_issue!, only: [:issue, :production] before_action :authorize_read_merge_request!, only: [:code, :review] + feature_category :planning_analytics + def issue render_events(cycle_analytics[:issue].events) end diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index ef97bc795f9..1ddc9d567e0 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -12,6 +12,8 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController track_unique_visits :show, target_id: 'p_analytics_valuestream' + feature_category :planning_analytics + def show @cycle_analytics = ::CycleAnalytics::ProjectLevel.new(@project, options: options(cycle_analytics_project_params)) diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index 4f4adaea56e..ce25f86d692 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -10,6 +10,8 @@ class Projects::DeployKeysController < Projects::ApplicationController layout 'project_settings' + feature_category :continuous_delivery + def index respond_to do |format| format.html { redirect_to_repository } diff --git a/app/controllers/projects/deploy_tokens_controller.rb b/app/controllers/projects/deploy_tokens_controller.rb index 830b1f4fe4a..3c890bbafdf 100644 --- a/app/controllers/projects/deploy_tokens_controller.rb +++ b/app/controllers/projects/deploy_tokens_controller.rb @@ -3,6 +3,8 @@ class Projects::DeployTokensController < Projects::ApplicationController before_action :authorize_admin_project! + feature_category :continuous_delivery + def revoke @token = @project.deploy_tokens.find(params[:id]) @token.revoke! diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb index 1344cf775e4..231684427fb 100644 --- a/app/controllers/projects/deployments_controller.rb +++ b/app/controllers/projects/deployments_controller.rb @@ -3,6 +3,8 @@ class Projects::DeploymentsController < Projects::ApplicationController before_action :authorize_read_deployment! + feature_category :continuous_delivery + # rubocop: disable CodeReuse/ActiveRecord def index deployments = environment.deployments.reorder(created_at: :desc) diff --git a/app/controllers/projects/design_management/designs_controller.rb b/app/controllers/projects/design_management/designs_controller.rb index fec09fa9515..550d8578396 100644 --- a/app/controllers/projects/design_management/designs_controller.rb +++ b/app/controllers/projects/design_management/designs_controller.rb @@ -3,6 +3,8 @@ class Projects::DesignManagement::DesignsController < Projects::ApplicationController before_action :authorize_read_design! + feature_category :design_management + private def authorize_read_design! diff --git a/app/controllers/projects/discussions_controller.rb b/app/controllers/projects/discussions_controller.rb index 06231607f73..b9ab1076999 100644 --- a/app/controllers/projects/discussions_controller.rb +++ b/app/controllers/projects/discussions_controller.rb @@ -9,6 +9,8 @@ class Projects::DiscussionsController < Projects::ApplicationController before_action :discussion, only: [:resolve, :unresolve] before_action :authorize_resolve_discussion!, only: [:resolve, :unresolve] + feature_category :issue_tracking + def resolve Discussions::ResolveService.new(project, current_user, one_or_more_discussions: discussion).execute diff --git a/app/controllers/projects/environments/prometheus_api_controller.rb b/app/controllers/projects/environments/prometheus_api_controller.rb index f0bb5360f84..97810d7d439 100644 --- a/app/controllers/projects/environments/prometheus_api_controller.rb +++ b/app/controllers/projects/environments/prometheus_api_controller.rb @@ -5,6 +5,8 @@ class Projects::Environments::PrometheusApiController < Projects::ApplicationCon before_action :proxyable + feature_category :metrics + private def proxyable diff --git a/app/controllers/projects/environments/sample_metrics_controller.rb b/app/controllers/projects/environments/sample_metrics_controller.rb index 9176c7cbd56..3df20810cb3 100644 --- a/app/controllers/projects/environments/sample_metrics_controller.rb +++ b/app/controllers/projects/environments/sample_metrics_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Projects::Environments::SampleMetricsController < Projects::ApplicationController + feature_category :metrics + def query result = Metrics::SampleMetricsService.new(params[:identifier], range_start: params[:start], range_end: params[:end]).query diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 71195fdb892..c37abf82fe9 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -25,6 +25,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController before_action :expire_etag_cache, only: [:index], unless: -> { request.format.json? } after_action :expire_etag_cache, only: [:cancel_auto_stop] + feature_category :continuous_delivery + def index @environments = project.environments .with_state(params[:scope] || :available) diff --git a/app/controllers/projects/error_tracking/base_controller.rb b/app/controllers/projects/error_tracking/base_controller.rb index 6efc6d00702..ffbe487d8a1 100644 --- a/app/controllers/projects/error_tracking/base_controller.rb +++ b/app/controllers/projects/error_tracking/base_controller.rb @@ -3,6 +3,8 @@ class Projects::ErrorTracking::BaseController < Projects::ApplicationController POLLING_INTERVAL = 1_000 + feature_category :error_tracking + def set_polling_interval Gitlab::PollingInterval.set_header(response, interval: POLLING_INTERVAL) end diff --git a/app/controllers/projects/error_tracking/projects_controller.rb b/app/controllers/projects/error_tracking/projects_controller.rb index 75a2c976d8b..d59cbc25d25 100644 --- a/app/controllers/projects/error_tracking/projects_controller.rb +++ b/app/controllers/projects/error_tracking/projects_controller.rb @@ -7,6 +7,8 @@ module Projects before_action :authorize_read_sentry_issue! + feature_category :error_tracking + def index service = ::ErrorTracking::ListProjectsService.new( project, diff --git a/app/controllers/projects/feature_flags_clients_controller.rb b/app/controllers/projects/feature_flags_clients_controller.rb new file mode 100644 index 00000000000..9a1f8932a27 --- /dev/null +++ b/app/controllers/projects/feature_flags_clients_controller.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class Projects::FeatureFlagsClientsController < Projects::ApplicationController + before_action :authorize_admin_feature_flags_client! + before_action :feature_flags_client + + feature_category :feature_flags + + def reset_token + feature_flags_client.reset_token! + + respond_to do |format| + format.json do + render json: feature_flags_client_token_json, status: :ok + end + end + end + + private + + def feature_flags_client + project.operations_feature_flags_client || not_found + end + + def feature_flags_client_token_json + FeatureFlagsClientSerializer.new + .represent_token(feature_flags_client) + end +end diff --git a/app/controllers/projects/feature_flags_controller.rb b/app/controllers/projects/feature_flags_controller.rb new file mode 100644 index 00000000000..e9d450a6ce3 --- /dev/null +++ b/app/controllers/projects/feature_flags_controller.rb @@ -0,0 +1,174 @@ +# frozen_string_literal: true + +class Projects::FeatureFlagsController < Projects::ApplicationController + respond_to :html + + before_action :authorize_read_feature_flag! + before_action :authorize_create_feature_flag!, only: [:new, :create] + before_action :authorize_update_feature_flag!, only: [:edit, :update] + before_action :authorize_destroy_feature_flag!, only: [:destroy] + + before_action :feature_flag, only: [:edit, :update, :destroy] + + before_action :ensure_legacy_flags_writable!, only: [:update] + + before_action do + push_frontend_feature_flag(:feature_flag_permissions) + push_frontend_feature_flag(:feature_flags_new_version, project, default_enabled: true) + push_frontend_feature_flag(:feature_flags_legacy_read_only, project, default_enabled: true) + push_frontend_feature_flag(:feature_flags_legacy_read_only_override, project) + end + + feature_category :feature_flags + + def index + @feature_flags = FeatureFlagsFinder + .new(project, current_user, scope: params[:scope]) + .execute + .page(params[:page]) + .per(30) + + respond_to do |format| + format.html + format.json do + Gitlab::PollingInterval.set_header(response, interval: 10_000) + + render json: { feature_flags: feature_flags_json }.merge(summary_json) + end + end + end + + def new + end + + def show + respond_to do |format| + format.json do + Gitlab::PollingInterval.set_header(response, interval: 10_000) + + render_success_json(feature_flag) + end + end + end + + def create + result = FeatureFlags::CreateService.new(project, current_user, create_params).execute + + if result[:status] == :success + respond_to do |format| + format.json { render_success_json(result[:feature_flag]) } + end + else + respond_to do |format| + format.json { render_error_json(result[:message]) } + end + end + end + + def edit + end + + def update + result = FeatureFlags::UpdateService.new(project, current_user, update_params).execute(feature_flag) + + if result[:status] == :success + respond_to do |format| + format.json { render_success_json(result[:feature_flag]) } + end + else + respond_to do |format| + format.json { render_error_json(result[:message]) } + end + end + end + + def destroy + result = FeatureFlags::DestroyService.new(project, current_user).execute(feature_flag) + + if result[:status] == :success + respond_to do |format| + format.html { redirect_to_index(notice: _('Feature flag was successfully removed.')) } + format.json { render_success_json(feature_flag) } + end + else + respond_to do |format| + format.html { redirect_to_index(alert: _('Feature flag was not removed.')) } + format.json { render_error_json(result[:message]) } + end + end + end + + protected + + def feature_flag + @feature_flag ||= @noteable = if new_version_feature_flags_enabled? + project.operations_feature_flags.find_by_iid!(params[:iid]) + else + project.operations_feature_flags.legacy_flag.find_by_iid!(params[:iid]) + end + end + + def new_version_feature_flags_enabled? + ::Feature.enabled?(:feature_flags_new_version, project, default_enabled: true) + end + + def ensure_legacy_flags_writable! + if ::Feature.enabled?(:feature_flags_legacy_read_only, project, default_enabled: true) && + ::Feature.disabled?(:feature_flags_legacy_read_only_override, project) && + feature_flag.legacy_flag? + render_error_json(['Legacy feature flags are read-only']) + end + 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]]) + 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]]) + end + + def feature_flag_json(feature_flag) + FeatureFlagSerializer + .new(project: @project, current_user: @current_user) + .represent(feature_flag) + end + + def feature_flags_json + FeatureFlagSerializer + .new(project: @project, current_user: @current_user) + .with_pagination(request, response) + .represent(@feature_flags) + end + + def summary_json + FeatureFlagSummarySerializer + .new(project: @project, current_user: @current_user) + .represent(@project) + end + + def redirect_to_index(**args) + redirect_to project_feature_flags_path(@project), status: :found, **args + end + + def render_success_json(feature_flag) + render json: feature_flag_json(feature_flag), status: :ok + end + + def render_error_json(messages) + render json: { message: messages }, + status: :bad_request + end +end diff --git a/app/controllers/projects/feature_flags_user_lists_controller.rb b/app/controllers/projects/feature_flags_user_lists_controller.rb new file mode 100644 index 00000000000..7be3254e966 --- /dev/null +++ b/app/controllers/projects/feature_flags_user_lists_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class Projects::FeatureFlagsUserListsController < Projects::ApplicationController + before_action :authorize_admin_feature_flags_user_lists! + before_action :user_list, only: [:edit, :show] + + feature_category :feature_flags + + def new + end + + def edit + end + + def show + end + + private + + def user_list + @user_list = project.operations_feature_flags_user_lists.find_by_iid!(params[:iid]) + end +end diff --git a/app/controllers/projects/find_file_controller.rb b/app/controllers/projects/find_file_controller.rb index c026e9ff332..89e72d98a33 100644 --- a/app/controllers/projects/find_file_controller.rb +++ b/app/controllers/projects/find_file_controller.rb @@ -10,6 +10,8 @@ class Projects::FindFileController < Projects::ApplicationController before_action :assign_ref_vars before_action :authorize_download_code! + feature_category :source_code_management + def show return render_404 unless @repository.commit(@ref) diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index c6b6b825bb7..1c2930f6e9b 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -14,6 +14,8 @@ class Projects::ForksController < Projects::ApplicationController before_action :authorize_fork_project!, only: [:new, :create] before_action :authorize_fork_namespace!, only: [:create] + feature_category :source_code_management + def index @total_forks_count = project.forks.size @public_forks_count = project.forks.public_only.size diff --git a/app/controllers/projects/grafana_api_controller.rb b/app/controllers/projects/grafana_api_controller.rb index c9870f1be2b..9c5d6c8ebc3 100644 --- a/app/controllers/projects/grafana_api_controller.rb +++ b/app/controllers/projects/grafana_api_controller.rb @@ -4,6 +4,8 @@ class Projects::GrafanaApiController < Projects::ApplicationController include RenderServiceResults include MetricsDashboard + feature_category :metrics + def proxy result = ::Grafana::ProxyService.new( project, diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index 9b889f9e837..2b030793c58 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -11,6 +11,8 @@ class Projects::GraphsController < Projects::ApplicationController track_unique_visits :charts, target_id: 'p_analytics_repo' + feature_category :source_code_management + def show respond_to do |format| format.html @@ -57,7 +59,6 @@ class Projects::GraphsController < Projects::ApplicationController end def get_daily_coverage_options - return unless Feature.enabled?(:ci_download_daily_code_coverage, @project, default_enabled: true) return unless can?(current_user, :read_build_report_results, project) date_today = Date.current diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb index a30c455a7e4..c6c90ffaba2 100644 --- a/app/controllers/projects/group_links_controller.rb +++ b/app/controllers/projects/group_links_controller.rb @@ -5,6 +5,8 @@ class Projects::GroupLinksController < Projects::ApplicationController before_action :authorize_admin_project! before_action :authorize_admin_project_member!, only: [:update] + feature_category :subgroups + def create group = Group.find(params[:link_group_id]) if params[:link_group_id].present? @@ -21,8 +23,17 @@ class Projects::GroupLinksController < Projects::ApplicationController end def update - @group_link = @project.project_group_links.find(params[:id]) - Projects::GroupLinks::UpdateService.new(@group_link).execute(group_link_params) + group_link = @project.project_group_links.find(params[:id]) + Projects::GroupLinks::UpdateService.new(group_link).execute(group_link_params) + + if group_link.expires? + render json: { + expires_in: helpers.distance_of_time_in_words_to_now(group_link.expires_at), + expires_soon: group_link.expires_soon? + } + else + render json: {} + end end def destroy diff --git a/app/controllers/projects/hook_logs_controller.rb b/app/controllers/projects/hook_logs_controller.rb index ed7e7b68acb..99ebe3335c0 100644 --- a/app/controllers/projects/hook_logs_controller.rb +++ b/app/controllers/projects/hook_logs_controller.rb @@ -12,6 +12,8 @@ class Projects::HookLogsController < Projects::ApplicationController layout 'project_settings' + feature_category :integrations + def show end diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 2f4dc1ddc3a..8dabf3e640b 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -12,6 +12,8 @@ class Projects::HooksController < Projects::ApplicationController layout "project_settings" + feature_category :integrations + def index @hooks = @project.hooks @hook = ProjectHook.new @@ -50,7 +52,7 @@ class Projects::HooksController < Projects::ApplicationController end def destroy - hook.destroy + destroy_hook(hook) redirect_to action: :index, status: :found end diff --git a/app/controllers/projects/import/jira_controller.rb b/app/controllers/projects/import/jira_controller.rb index 976ac7df976..8418a607659 100644 --- a/app/controllers/projects/import/jira_controller.rb +++ b/app/controllers/projects/import/jira_controller.rb @@ -7,6 +7,8 @@ module Projects before_action :authorize_read_project! before_action :validate_jira_import_settings! + feature_category :integrations + def show end diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb index 9bed12fd151..6cdd1c0bc8c 100644 --- a/app/controllers/projects/imports_controller.rb +++ b/app/controllers/projects/imports_controller.rb @@ -11,6 +11,8 @@ class Projects::ImportsController < Projects::ApplicationController before_action :redirect_if_progress, except: :show before_action :redirect_if_no_import, only: :show + feature_category :importers + def new end diff --git a/app/controllers/projects/incident_management/pager_duty_incidents_controller.rb b/app/controllers/projects/incident_management/pager_duty_incidents_controller.rb index dac1640dd08..f1264ca4a45 100644 --- a/app/controllers/projects/incident_management/pager_duty_incidents_controller.rb +++ b/app/controllers/projects/incident_management/pager_duty_incidents_controller.rb @@ -10,6 +10,8 @@ module Projects prepend_before_action :project_without_auth + feature_category :incident_management + def create result = webhook_processor.execute(params[:token]) diff --git a/app/controllers/projects/incidents_controller.rb b/app/controllers/projects/incidents_controller.rb index 12cc4dde1f4..3395e75666e 100644 --- a/app/controllers/projects/incidents_controller.rb +++ b/app/controllers/projects/incidents_controller.rb @@ -1,8 +1,45 @@ # frozen_string_literal: true class Projects::IncidentsController < Projects::ApplicationController - before_action :authorize_read_incidents! + include IssuableActions + include Gitlab::Utils::StrongMemoize + + before_action :authorize_read_issue! + before_action :load_incident, only: [:show] + + feature_category :incident_management def index end + + private + + def incident + strong_memoize(:incident) do + incident_finder + .execute + .inc_relations_for_view + .iid_in(params[:id]) + .without_order + .first + end + end + + def load_incident + @issue = incident # needed by rendered view + return render_404 unless can?(current_user, :read_issue, incident) + + @noteable = incident + @note = incident.project.notes.new(noteable: issuable) + end + + alias_method :issuable, :incident + + def incident_finder + IssuesFinder.new(current_user, project_id: @project.id, issue_types: :incident) + end + + def serializer + IssueSerializer.new(current_user: current_user, project: incident.project) + end end diff --git a/app/controllers/projects/issue_links_controller.rb b/app/controllers/projects/issue_links_controller.rb index 2f7489373ed..35f3e00fae7 100644 --- a/app/controllers/projects/issue_links_controller.rb +++ b/app/controllers/projects/issue_links_controller.rb @@ -7,6 +7,8 @@ module Projects before_action :authorize_admin_issue_link!, only: [:create, :destroy] before_action :authorize_issue_link_association!, only: :destroy + feature_category :issue_tracking + private def authorize_admin_issue_link! diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 7f0d23b79ce..9a8965dbeb6 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -44,8 +44,6 @@ class Projects::IssuesController < Projects::ApplicationController push_frontend_feature_flag(:vue_issuable_sidebar, project.group) push_frontend_feature_flag(:tribute_autocomplete, @project) push_frontend_feature_flag(:vue_issuables_list, project) - push_frontend_feature_flag(:design_management_todo_button, project, default_enabled: true) - push_frontend_feature_flag(:vue_sidebar_labels, @project) end before_action only: :show do @@ -53,10 +51,13 @@ class Projects::IssuesController < Projects::ApplicationController real_time_enabled = Gitlab::ActionCable::Config.in_app? || Feature.enabled?(real_time_feature_flag, @project) gon.push({ features: { real_time_feature_flag.to_s.camelize(:lower) => real_time_enabled } }, true) + + record_experiment_user(:invite_members_version_a) + record_experiment_user(:invite_members_version_b) end before_action only: :index do - push_frontend_feature_flag(:scoped_labels, @project) + push_frontend_feature_flag(:scoped_labels, @project, type: :licensed) end around_action :allow_gitaly_ref_name_caching, only: [:discussions] @@ -65,6 +66,17 @@ class Projects::IssuesController < Projects::ApplicationController alias_method :designs, :show + feature_category :issue_tracking, [ + :index, :calendar, :show, :new, :create, :edit, :update, + :destroy, :move, :reorder, :designs, :toggle_subscription, + :discussions, :bulk_update, :realtime_changes, + :toggle_award_emoji, :mark_as_spam, :related_branches, + :can_create_branch, :create_merge_request + ] + + feature_category :service_desk, [:service_desk] + feature_category :importers, [:import_csv, :export_csv] + def index @issues = @issuables @@ -204,7 +216,7 @@ class Projects::IssuesController < Projects::ApplicationController end def export_csv - ExportCsvWorker.perform_async(current_user.id, project.id, finder_options.to_h) # rubocop:disable CodeReuse/Worker + IssuableExportCsvWorker.perform_async(:issue, current_user.id, project.id, finder_options.to_h) # rubocop:disable CodeReuse/Worker index_path = project_issues_path(project) message = _('Your CSV export has started. It will be emailed to %{email} when complete.') % { email: current_user.notification_email } @@ -239,7 +251,7 @@ class Projects::IssuesController < Projects::ApplicationController return @issue if defined?(@issue) # The Sortable default scope causes performance issues when used with find_by - @issuable = @noteable = @issue ||= @project.issues.includes(author: :status).where(iid: params[:id]).reorder(nil).take! + @issuable = @noteable = @issue ||= @project.issues.inc_relations_for_view.iid_in(params[:id]).without_order.take! @note = @project.notes.new(noteable: @issuable) return render_404 unless can?(current_user, :read_issue, @issue) @@ -313,7 +325,7 @@ class Projects::IssuesController < Projects::ApplicationController end def store_uri - if request.get? && !request.xhr? + if request.get? && request.format.html? store_location_for :user, request.fullpath end end diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index 3f7f8da3478..3ceb60a6aef 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -4,7 +4,8 @@ class Projects::JobsController < Projects::ApplicationController include SendFileUpload include ContinueParams - before_action :build, except: [:index] + before_action :find_job_as_build, except: [:index, :play] + before_action :find_job_as_processable, only: [:play] before_action :authorize_read_build! before_action :authorize_update_build!, except: [:index, :show, :status, :raw, :trace, :erase] @@ -16,6 +17,8 @@ class Projects::JobsController < Projects::ApplicationController layout 'project' + feature_category :continuous_integration + def index # We need all builds for tabs counters @all_builds = Ci::JobsFinder.new(current_user: current_user, project: @project).execute @@ -28,11 +31,6 @@ class Projects::JobsController < Projects::ApplicationController # rubocop: disable CodeReuse/ActiveRecord def show - @pipeline = @build.pipeline - @builds = @pipeline.builds - .order('id DESC') - .present(current_user: current_user) - respond_to do |format| format.html format.json do @@ -47,10 +45,10 @@ class Projects::JobsController < Projects::ApplicationController # rubocop: enable CodeReuse/ActiveRecord def trace - build.trace.read do |stream| + @build.trace.read do |stream| respond_to do |format| format.json do - build.trace.being_watched! + @build.trace.being_watched! build_trace = Ci::BuildTrace.new( build: @build, @@ -75,8 +73,13 @@ class Projects::JobsController < Projects::ApplicationController def play return respond_422 unless @build.playable? - build = @build.play(current_user, play_params[:job_variables_attributes]) - redirect_to build_path(build) + job = @build.play(current_user, play_params[:job_variables_attributes]) + + if job.is_a?(Ci::Bridge) + redirect_to pipeline_path(job.pipeline) + else + redirect_to build_path(job) + end end def cancel @@ -120,7 +123,7 @@ class Projects::JobsController < Projects::ApplicationController send_params: raw_send_params, redirect_params: raw_redirect_params) else - build.trace.read do |stream| + @build.trace.read do |stream| if stream.file? workhorse_set_content_type! send_file stream.path, type: 'text/plain; charset=utf-8', disposition: 'inline' @@ -152,19 +155,19 @@ class Projects::JobsController < Projects::ApplicationController private def authorize_update_build! - return access_denied! unless can?(current_user, :update_build, build) + return access_denied! unless can?(current_user, :update_build, @build) end def authorize_erase_build! - return access_denied! unless can?(current_user, :erase_build, build) + return access_denied! unless can?(current_user, :erase_build, @build) end def authorize_use_build_terminal! - return access_denied! unless can?(current_user, :create_build_terminal, build) + return access_denied! unless can?(current_user, :create_build_terminal, @build) end def authorize_create_proxy_build! - return access_denied! unless can?(current_user, :create_build_service_proxy, build) + return access_denied! unless can?(current_user, :create_build_service_proxy, @build) end def verify_api_request! @@ -189,14 +192,22 @@ class Projects::JobsController < Projects::ApplicationController end def trace_artifact_file - @trace_artifact_file ||= build.job_artifacts_trace&.file + @trace_artifact_file ||= @build.job_artifacts_trace&.file end - def build - @build ||= project.builds.find(params[:id]) + def find_job_as_build + @build = project.builds.find(params[:id]) .present(current_user: current_user) end + def find_job_as_processable + if ::Gitlab::Ci::Features.manual_bridges_enabled?(project) + @build = project.processables.find(params[:id]) + else + find_job_as_build + end + end + def build_path(build) project_job_path(build.project, build) end @@ -211,10 +222,10 @@ 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 b7aeab8f5ff..ba8e6b90971 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -2,6 +2,7 @@ class Projects::LabelsController < Projects::ApplicationController include ToggleSubscriptionAction + include ShowInheritedLabelsChecker before_action :check_issuables_available! before_action :label, only: [:edit, :update, :destroy, :promote] @@ -14,6 +15,8 @@ class Projects::LabelsController < Projects::ApplicationController respond_to :js, :html + feature_category :issue_tracking + def index @prioritized_labels = @available_labels.prioritized(@project) @labels = @available_labels.unprioritized(@project).page(params[:page]) @@ -161,7 +164,7 @@ class Projects::LabelsController < Projects::ApplicationController @available_labels ||= LabelsFinder.new(current_user, project_id: @project.id, - include_ancestor_groups: params[:include_ancestor_groups], + include_ancestor_groups: show_inherited_labels?(params[:include_ancestor_groups]), search: params[:search], subscribed: params[:subscribed], sort: sort).execute diff --git a/app/controllers/projects/logs_controller.rb b/app/controllers/projects/logs_controller.rb index b9027b3a2cb..b9aa9bfc947 100644 --- a/app/controllers/projects/logs_controller.rb +++ b/app/controllers/projects/logs_controller.rb @@ -7,6 +7,8 @@ module Projects before_action :authorize_read_pod_logs! before_action :ensure_deployments, only: %i(k8s elasticsearch) + feature_category :logging + def index if environment || cluster render :index diff --git a/app/controllers/projects/mattermosts_controller.rb b/app/controllers/projects/mattermosts_controller.rb index cfaeddf711a..63a36732d87 100644 --- a/app/controllers/projects/mattermosts_controller.rb +++ b/app/controllers/projects/mattermosts_controller.rb @@ -10,6 +10,8 @@ class Projects::MattermostsController < Projects::ApplicationController before_action :service before_action :teams, only: [:new] + feature_category :integrations + def new end diff --git a/app/controllers/projects/merge_requests/application_controller.rb b/app/controllers/projects/merge_requests/application_controller.rb index 921da788ad2..9cac9f37eb7 100644 --- a/app/controllers/projects/merge_requests/application_controller.rb +++ b/app/controllers/projects/merge_requests/application_controller.rb @@ -5,6 +5,8 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont before_action :merge_request before_action :authorize_read_merge_request! + feature_category :code_review + private def merge_request @@ -35,6 +37,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont :source_branch, :source_project_id, :state_event, + :wip_event, :squash, :target_branch, :target_project_id, diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index 8aacfdce094..07c38431f0f 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -64,7 +64,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic render: ->(partial, locals) { view_to_html_string(partial, locals) } } - options = additional_attributes.merge(diff_view: Feature.enabled?(:unified_diff_lines, @merge_request.project) ? "inline" : diff_view) + options = additional_attributes.merge(diff_view: Feature.enabled?(:unified_diff_lines, @merge_request.project, default_enabled: true) ? "inline" : diff_view) if @merge_request.project.context_commits_enabled? options[:context_commits] = @merge_request.recent_context_commits @@ -173,7 +173,6 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic end def update_diff_discussion_positions! - return unless Feature.enabled?(:merge_ref_head_comments, @merge_request.target_project, default_enabled: true) return unless Feature.enabled?(:merge_red_head_comments_position_on_demand, @merge_request.target_project, default_enabled: true) return if @merge_request.has_any_diff_note_positions? diff --git a/app/controllers/projects/merge_requests/drafts_controller.rb b/app/controllers/projects/merge_requests/drafts_controller.rb index f4846b1aa81..ca3f36cafe1 100644 --- a/app/controllers/projects/merge_requests/drafts_controller.rb +++ b/app/controllers/projects/merge_requests/drafts_controller.rb @@ -45,7 +45,7 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli if result[:status] == :success head :ok else - render json: { message: result[:message] }, status: result[:status] + render json: { message: result[:message] }, status: :internal_server_error end end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 92785540172..91a041bb35b 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -27,11 +27,8 @@ 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: [:show] do - push_frontend_feature_flag(:deploy_from_footer, @project, default_enabled: true) - push_frontend_feature_flag(:suggest_pipeline) if experiment_enabled?(:suggest_pipeline) - push_frontend_feature_flag(:code_navigation, @project, default_enabled: true) + push_frontend_experiment(:suggest_pipeline) push_frontend_feature_flag(:widget_visibility_polling, @project, default_enabled: true) - push_frontend_feature_flag(:merge_ref_head_comments, @project, default_enabled: true) push_frontend_feature_flag(:mr_commit_neighbor_nav, @project, default_enabled: true) push_frontend_feature_flag(:multiline_comments, @project, default_enabled: true) push_frontend_feature_flag(:file_identifier_hash) @@ -39,25 +36,35 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:approvals_commented_by, @project, default_enabled: true) push_frontend_feature_flag(:hide_jump_to_next_unresolved_in_threads, default_enabled: true) push_frontend_feature_flag(:merge_request_widget_graphql, @project) - push_frontend_feature_flag(:unified_diff_lines, @project) + push_frontend_feature_flag(:unified_diff_lines, @project, default_enabled: true) push_frontend_feature_flag(:highlight_current_diff_row, @project) push_frontend_feature_flag(:default_merge_ref_for_diffs, @project) + push_frontend_feature_flag(:core_security_mr_widget, @project, default_enabled: true) + + record_experiment_user(:invite_members_version_a) + record_experiment_user(:invite_members_version_b) end before_action do push_frontend_feature_flag(:vue_issuable_sidebar, @project.group) + push_frontend_feature_flag(:deployment_filters) end around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :discussions] after_action :log_merge_request_show, only: [:show] - feature_category :source_code_management, - unless: -> (action) { action.ends_with?("_reports") } - feature_category :code_testing, - only: [:test_reports, :coverage_reports, :terraform_reports] - feature_category :accessibility_testing, - only: [:accessibility_reports] + feature_category :code_review, [ + :assign_related_issues, :bulk_update, :cancel_auto_merge, + :ci_environments_status, :commit_change_content, :commits, + :context_commits, :destroy, :diff_for_path, :discussions, + :edit, :exposed_artifacts, :index, :merge, + :pipeline_status, :pipelines, :rebase, :remove_wip, :show, + :toggle_award_emoji, :toggle_subscription, :update + ] + + feature_category :code_testing, [:test_reports, :coverage_reports, :terraform_reports] + feature_category :accessibility_testing, [:accessibility_reports] def index @merge_requests = @issuables diff --git a/app/controllers/projects/metrics/dashboards/builder_controller.rb b/app/controllers/projects/metrics/dashboards/builder_controller.rb index 2ab574d7d10..96ca6d89111 100644 --- a/app/controllers/projects/metrics/dashboards/builder_controller.rb +++ b/app/controllers/projects/metrics/dashboards/builder_controller.rb @@ -6,6 +6,8 @@ module Projects class BuilderController < Projects::ApplicationController before_action :authorize_metrics_dashboard! + feature_category :metrics + def panel_preview respond_to do |format| format.json do diff --git a/app/controllers/projects/metrics_dashboard_controller.rb b/app/controllers/projects/metrics_dashboard_controller.rb index bc0a701b9fd..3f10749602e 100644 --- a/app/controllers/projects/metrics_dashboard_controller.rb +++ b/app/controllers/projects/metrics_dashboard_controller.rb @@ -14,6 +14,8 @@ module Projects push_frontend_feature_flag(:disable_metric_dashboard_refresh_rate) end + feature_category :metrics + def show if environment render 'projects/environments/metrics' diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 16d63cc184f..e6c4af00b29 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -5,7 +5,7 @@ class Projects::MilestonesController < Projects::ApplicationController include MilestoneActions before_action :check_issuables_available! - before_action :milestone, only: [:edit, :update, :destroy, :show, :merge_requests, :participants, :labels, :promote] + before_action :milestone, only: [:edit, :update, :destroy, :show, :issues, :merge_requests, :participants, :labels, :promote] before_action do push_frontend_feature_flag(:burnup_charts, @project) end @@ -14,13 +14,15 @@ class Projects::MilestonesController < Projects::ApplicationController before_action :authorize_read_milestone! # Allow admin milestone - before_action :authorize_admin_milestone!, except: [:index, :show, :merge_requests, :participants, :labels] + before_action :authorize_admin_milestone!, except: [:index, :show, :issues, :merge_requests, :participants, :labels] # Allow to promote milestone before_action :authorize_promote_milestone!, only: :promote respond_to :html + feature_category :issue_tracking + def index @sort = params[:sort] || 'due_date_asc' @milestones = milestones.sort_by_attribute(@sort) diff --git a/app/controllers/projects/mirrors_controller.rb b/app/controllers/projects/mirrors_controller.rb index 518e6a92afa..01abb72fc86 100644 --- a/app/controllers/projects/mirrors_controller.rb +++ b/app/controllers/projects/mirrors_controller.rb @@ -10,6 +10,8 @@ class Projects::MirrorsController < Projects::ApplicationController layout "project_settings" + feature_category :source_code_management + def show redirect_to_repository_settings(project, anchor: 'js-push-remote-settings') end diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb index 47788438da9..89b679fc033 100644 --- a/app/controllers/projects/network_controller.rb +++ b/app/controllers/projects/network_controller.rb @@ -11,6 +11,8 @@ class Projects::NetworkController < Projects::ApplicationController before_action :assign_options before_action :assign_commit + feature_category :source_code_management + def show @url = project_network_path(@project, @ref, @options.merge(format: :json)) @commit_url = project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s") diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index c0b8c9e550d..e50e293a103 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -11,6 +11,8 @@ class Projects::NotesController < Projects::ApplicationController before_action :authorize_create_note!, only: [:create] before_action :authorize_resolve_note!, only: [:resolve, :unresolve] + feature_category :issue_tracking + def delete_attachment note.remove_attachment! note.update_attribute(:attachment, nil) diff --git a/app/controllers/projects/packages/package_files_controller.rb b/app/controllers/projects/packages/package_files_controller.rb index dd6d875cd1e..32aadb4fcf4 100644 --- a/app/controllers/projects/packages/package_files_controller.rb +++ b/app/controllers/projects/packages/package_files_controller.rb @@ -6,6 +6,8 @@ module Projects include PackagesAccess include SendFileUpload + feature_category :package_registry + def download package_file = project.package_files.find(params[:id]) diff --git a/app/controllers/projects/packages/packages_controller.rb b/app/controllers/projects/packages/packages_controller.rb index fc4ef7a01dc..15dc11f5df8 100644 --- a/app/controllers/projects/packages/packages_controller.rb +++ b/app/controllers/projects/packages/packages_controller.rb @@ -5,20 +5,13 @@ module Projects class PackagesController < Projects::ApplicationController include PackagesAccess - before_action :authorize_destroy_package!, only: [:destroy] + feature_category :package_registry def show @package = project.packages.find(params[:id]) @package_files = @package.package_files.recent @maven_metadatum = @package.maven_metadatum end - - def destroy - @package = project.packages.find(params[:id]) - @package.destroy - - redirect_to project_packages_path(@project), status: :found, notice: _('Package was removed') - end end end end diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb index 9ad6bf4095a..0aac517e3e3 100644 --- a/app/controllers/projects/pages_controller.rb +++ b/app/controllers/projects/pages_controller.rb @@ -8,6 +8,8 @@ class Projects::PagesController < Projects::ApplicationController before_action :authorize_update_pages!, except: [:show, :destroy] before_action :authorize_remove_pages!, only: [:destroy] + feature_category :pages + # rubocop: disable CodeReuse/ActiveRecord def show @domains = @project.pages_domains.order(:domain).present(current_user: current_user) diff --git a/app/controllers/projects/pages_domains_controller.rb b/app/controllers/projects/pages_domains_controller.rb index cccf8fe4358..a6b22a28b17 100644 --- a/app/controllers/projects/pages_domains_controller.rb +++ b/app/controllers/projects/pages_domains_controller.rb @@ -9,6 +9,8 @@ class Projects::PagesDomainsController < Projects::ApplicationController helper_method :domain_presenter + feature_category :pages + def show end diff --git a/app/controllers/projects/performance_monitoring/dashboards_controller.rb b/app/controllers/projects/performance_monitoring/dashboards_controller.rb index ec5a33f5dd6..51a07c1b7a5 100644 --- a/app/controllers/projects/performance_monitoring/dashboards_controller.rb +++ b/app/controllers/projects/performance_monitoring/dashboards_controller.rb @@ -12,6 +12,8 @@ module Projects respond_error(http_status: :bad_request, message: _('Request parameter %{param} is missing.') % { param: exception.param }) end + feature_category :metrics + def create result = ::Metrics::Dashboard::CloneDashboardService.new(project, current_user, dashboard_params).execute diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb index e7e8a900060..5655d3b4c0d 100644 --- a/app/controllers/projects/pipeline_schedules_controller.rb +++ b/app/controllers/projects/pipeline_schedules_controller.rb @@ -10,6 +10,8 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController before_action :authorize_update_pipeline_schedule!, except: [:index, :new, :create, :play] before_action :authorize_admin_pipeline_schedule!, only: [:destroy] + feature_category :continuous_integration + # rubocop: disable CodeReuse/ActiveRecord def index @scope = params[:scope] diff --git a/app/controllers/projects/pipelines/application_controller.rb b/app/controllers/projects/pipelines/application_controller.rb index 92887750813..c147d697888 100644 --- a/app/controllers/projects/pipelines/application_controller.rb +++ b/app/controllers/projects/pipelines/application_controller.rb @@ -10,6 +10,8 @@ module Projects before_action :pipeline before_action :authorize_read_pipeline! + feature_category :continuous_integration + private def pipeline diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index c1734d2cd8a..953dce4d63c 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -5,17 +5,19 @@ class Projects::PipelinesController < Projects::ApplicationController include Analytics::UniqueVisitsHelper before_action :whitelist_query_limiting, only: [:create, :retry] - before_action :pipeline, except: [:index, :new, :create, :charts] + before_action :pipeline, except: [:index, :new, :create, :charts, :config_variables] before_action :set_pipeline_path, only: [:show] before_action :authorize_read_pipeline! before_action :authorize_read_build!, only: [:index] - before_action :authorize_create_pipeline!, only: [:new, :create] + before_action :authorize_create_pipeline!, only: [:new, :create, :config_variables] before_action :authorize_update_pipeline!, only: [:retry, :cancel] before_action do push_frontend_feature_flag(:filter_pipelines_search, project, default_enabled: true) push_frontend_feature_flag(:dag_pipeline_tab, project, default_enabled: true) push_frontend_feature_flag(:pipelines_security_report_summary, project) - push_frontend_feature_flag(:new_pipeline_form) + push_frontend_feature_flag(:new_pipeline_form, project) + push_frontend_feature_flag(:graphql_pipeline_header, project, type: :development, default_enabled: false) + push_frontend_feature_flag(:new_pipeline_form_prefilled_vars, project, type: :development) end before_action :ensure_pipeline, only: [:show] @@ -30,6 +32,8 @@ class Projects::PipelinesController < Projects::ApplicationController POLLING_INTERVAL = 10_000 + feature_category :continuous_integration + def index @pipelines = Ci::PipelinesFinder .new(project, current_user, index_params) @@ -206,6 +210,14 @@ class Projects::PipelinesController < Projects::ApplicationController end end + def config_variables + respond_to do |format| + format.json do + render json: Ci::ListConfigVariablesService.new(@project).execute(params[:sha]) + end + end + end + private def serialize_pipelines diff --git a/app/controllers/projects/pipelines_settings_controller.rb b/app/controllers/projects/pipelines_settings_controller.rb index 7f988110977..6e08a889520 100644 --- a/app/controllers/projects/pipelines_settings_controller.rb +++ b/app/controllers/projects/pipelines_settings_controller.rb @@ -3,6 +3,8 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController before_action :authorize_admin_pipeline! + feature_category :continuous_integration + def show redirect_to project_settings_ci_cd_path(@project, params: params.to_unsafe_h) end diff --git a/app/controllers/projects/product_analytics_controller.rb b/app/controllers/projects/product_analytics_controller.rb index c019dc191d6..5db7585d8e0 100644 --- a/app/controllers/projects/product_analytics_controller.rb +++ b/app/controllers/projects/product_analytics_controller.rb @@ -5,6 +5,8 @@ class Projects::ProductAnalyticsController < Projects::ApplicationController before_action :authorize_read_product_analytics! before_action :tracker_variables, only: [:setup, :test] + feature_category :product_analytics + def index @events = product_analytics_events.order_by_time.page(params[:page]) end diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 3e52248f292..631f627838b 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -8,6 +8,8 @@ class Projects::ProjectMembersController < Projects::ApplicationController # Authorize before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access] + feature_category :authentication_and_authorization + def index @sort = params[:sort].presence || sort_value_name @@ -55,6 +57,10 @@ class Projects::ProjectMembersController < Projects::ApplicationController def filter_params params.permit(:search).merge(sort: @sort) end + + def membershipable_members + project.members + end end Projects::ProjectMembersController.prepend_if_ee('EE::Projects::ProjectMembersController') diff --git a/app/controllers/projects/prometheus/alerts_controller.rb b/app/controllers/projects/prometheus/alerts_controller.rb index c6ae65f7832..2892542e63c 100644 --- a/app/controllers/projects/prometheus/alerts_controller.rb +++ b/app/controllers/projects/prometheus/alerts_controller.rb @@ -16,6 +16,8 @@ module Projects before_action :authorize_read_prometheus_alerts!, except: [:notify] before_action :alert, only: [:update, :show, :destroy, :metrics_dashboard] + feature_category :alert_management + def index render json: serialize_as_json(alerts) end diff --git a/app/controllers/projects/prometheus/metrics_controller.rb b/app/controllers/projects/prometheus/metrics_controller.rb index 0340cb5beb0..d70d29a341f 100644 --- a/app/controllers/projects/prometheus/metrics_controller.rb +++ b/app/controllers/projects/prometheus/metrics_controller.rb @@ -6,6 +6,8 @@ module Projects before_action :authorize_admin_project! before_action :require_prometheus_metrics! + feature_category :metrics + def active_common respond_to do |format| format.json do diff --git a/app/controllers/projects/protected_refs_controller.rb b/app/controllers/projects/protected_refs_controller.rb index 060403a9cd9..4cba1a75330 100644 --- a/app/controllers/projects/protected_refs_controller.rb +++ b/app/controllers/projects/protected_refs_controller.rb @@ -10,6 +10,8 @@ class Projects::ProtectedRefsController < Projects::ApplicationController layout "project_settings" + feature_category :source_code_management + def index redirect_to_repository_settings(@project) end @@ -62,7 +64,7 @@ class Projects::ProtectedRefsController < Projects::ApplicationController end def access_level_attributes - %i[access_level id _destroy] + %i[access_level id _destroy deploy_key_id] end end diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index 29f1e4bfd44..a9490c106d4 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -15,6 +15,8 @@ class Projects::RawController < Projects::ApplicationController before_action :no_cache_headers, only: [:show] before_action :redirect_to_external_storage, only: :show, if: :static_objects_external_storage_enabled? + feature_category :source_code_management + def show @blob = @repository.blob_at(@commit.id, @path) diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index db770d3e438..4d23c853334 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -11,6 +11,8 @@ class Projects::RefsController < Projects::ApplicationController before_action :assign_ref_vars before_action :authorize_download_code! + feature_category :source_code_management + def switch respond_to do |format| format.html do diff --git a/app/controllers/projects/registry/application_controller.rb b/app/controllers/projects/registry/application_controller.rb index 2f891d78c91..e7bf8c8e757 100644 --- a/app/controllers/projects/registry/application_controller.rb +++ b/app/controllers/projects/registry/application_controller.rb @@ -8,6 +8,8 @@ module Projects before_action :verify_registry_enabled! before_action :authorize_read_container_image! + feature_category :container_registry + private def verify_registry_enabled! diff --git a/app/controllers/projects/registry/repositories_controller.rb b/app/controllers/projects/registry/repositories_controller.rb index 19d0cb9acdc..28a86ecc9f0 100644 --- a/app/controllers/projects/registry/repositories_controller.rb +++ b/app/controllers/projects/registry/repositories_controller.rb @@ -3,6 +3,8 @@ module Projects module Registry class RepositoriesController < ::Projects::Registry::ApplicationController + include PackagesHelper + before_action :authorize_update_container_image!, only: [:destroy] before_action :ensure_root_container_repository!, only: [:index] @@ -13,7 +15,7 @@ module Projects @images = ContainerRepositoriesFinder.new(user: current_user, subject: project, params: params.slice(:name)) .execute - track_event(:list_repositories) + track_package_event(:list_repositories, :container) serializer = ContainerRepositoriesSerializer .new(project: project, current_user: current_user) @@ -31,7 +33,7 @@ module Projects def destroy image.delete_scheduled! DeleteContainerRepositoryWorker.perform_async(current_user.id, image.id) # rubocop:disable CodeReuse/Worker - track_event(:delete_repository) + track_package_event(:delete_repository, :container) respond_to do |format| format.json { head :no_content } diff --git a/app/controllers/projects/registry/tags_controller.rb b/app/controllers/projects/registry/tags_controller.rb index c42e3f6bdba..ebdb668207f 100644 --- a/app/controllers/projects/registry/tags_controller.rb +++ b/app/controllers/projects/registry/tags_controller.rb @@ -3,12 +3,15 @@ module Projects module Registry class TagsController < ::Projects::Registry::ApplicationController + include PackagesHelper + before_action :authorize_destroy_container_image!, only: [:destroy] LIMIT = 15 def index - track_event(:list_tags) + track_package_event(:list_tags, :tag) + respond_to do |format| format.json do render json: ContainerTagsSerializer @@ -23,7 +26,7 @@ module Projects result = Projects::ContainerRepository::DeleteTagsService .new(image.project, current_user, tags: [params[:id]]) .execute(image) - track_event(:delete_tag) + track_package_event(:delete_tag, :tag) respond_to do |format| format.json { head(result[:status] == :success ? :ok : bad_request) } @@ -40,7 +43,7 @@ module Projects result = Projects::ContainerRepository::DeleteTagsService .new(image.project, current_user, tags: tag_names) .execute(image) - track_event(:delete_tag_bulk) + track_package_event(:delete_tag_bulk, :tag) respond_to do |format| format.json { head(result[:status] == :success ? :no_content : :bad_request) } diff --git a/app/controllers/projects/releases/evidences_controller.rb b/app/controllers/projects/releases/evidences_controller.rb index 34e450d903f..1e2dbf8047c 100644 --- a/app/controllers/projects/releases/evidences_controller.rb +++ b/app/controllers/projects/releases/evidences_controller.rb @@ -7,6 +7,8 @@ module Projects before_action :release before_action :authorize_read_release_evidence! + feature_category :release_evidence + def show respond_to do |format| format.json do diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb index bd24aae980c..4e8260d9e53 100644 --- a/app/controllers/projects/releases_controller.rb +++ b/app/controllers/projects/releases_controller.rb @@ -6,18 +6,16 @@ class Projects::ReleasesController < Projects::ApplicationController before_action :release, only: %i[edit show update downloads] before_action :authorize_read_release! before_action do - push_frontend_feature_flag(:release_issue_summary, project, default_enabled: true) - push_frontend_feature_flag(:release_evidence_collection, project, default_enabled: true) - push_frontend_feature_flag(:release_show_page, project, default_enabled: true) - push_frontend_feature_flag(:release_asset_link_editing, project, default_enabled: true) - push_frontend_feature_flag(:release_asset_link_type, project, default_enabled: true) push_frontend_feature_flag(:graphql_release_data, project, default_enabled: true) push_frontend_feature_flag(:graphql_milestone_stats, project, default_enabled: true) - push_frontend_feature_flag(:graphql_releases_page, project, default_enabled: false) + push_frontend_feature_flag(:graphql_releases_page, project, default_enabled: true) + push_frontend_feature_flag(:graphql_individual_release_page, project, default_enabled: true) end before_action :authorize_update_release!, only: %i[edit update] before_action :authorize_create_release!, only: :new + feature_category :release_orchestration + def index respond_to do |format| format.html do @@ -27,10 +25,6 @@ class Projects::ReleasesController < Projects::ApplicationController end end - def show - return render_404 unless Feature.enabled?(:release_show_page, project, default_enabled: true) - end - def new unless Feature.enabled?(:new_release_page, project, default_enabled: true) redirect_to(new_project_tag_path(@project)) diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb index 303326bbe23..ba3ab52e3af 100644 --- a/app/controllers/projects/repositories_controller.rb +++ b/app/controllers/projects/repositories_controller.rb @@ -18,6 +18,8 @@ class Projects::RepositoriesController < Projects::ApplicationController before_action :authorize_admin_project!, only: :create before_action :redirect_to_external_storage, only: :archive, if: :static_objects_external_storage_enabled? + feature_category :source_code_management + def create @project.create_repository diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb index cbeb32fd610..d225d5e104c 100644 --- a/app/controllers/projects/runner_projects_controller.rb +++ b/app/controllers/projects/runner_projects_controller.rb @@ -5,6 +5,8 @@ class Projects::RunnerProjectsController < Projects::ApplicationController layout 'project_settings' + feature_category :continuous_integration + def create @runner = Ci::Runner.find(params[:runner_project][:runner_id]) diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index ca62f54813b..544074f9840 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -6,6 +6,8 @@ class Projects::RunnersController < Projects::ApplicationController layout 'project_settings' + feature_category :continuous_integration + def index redirect_to project_settings_ci_cd_path(@project, anchor: 'js-runners-settings') end @@ -50,6 +52,10 @@ class Projects::RunnersController < Projects::ApplicationController end def toggle_shared_runners + if Feature.enabled?(:disable_shared_runners_on_group, default_enabled: true) && !project.shared_runners_enabled && project.group && project.group.shared_runners_setting == 'disabled_and_unoverridable' + return redirect_to project_runners_path(@project), alert: _("Cannot enable shared runners because parent group does not allow it") + end + project.toggle!(:shared_runners_enabled) redirect_to project_settings_ci_cd_path(@project, anchor: 'js-runners-settings') diff --git a/app/controllers/projects/serverless/functions_controller.rb b/app/controllers/projects/serverless/functions_controller.rb index 0b55414d390..4168880001c 100644 --- a/app/controllers/projects/serverless/functions_controller.rb +++ b/app/controllers/projects/serverless/functions_controller.rb @@ -5,6 +5,8 @@ module Projects class FunctionsController < Projects::ApplicationController before_action :authorize_read_cluster! + feature_category :serverless + def index respond_to do |format| format.json do diff --git a/app/controllers/projects/service_desk_controller.rb b/app/controllers/projects/service_desk_controller.rb index bcd190bbc2c..f7c0a54fb9e 100644 --- a/app/controllers/projects/service_desk_controller.rb +++ b/app/controllers/projects/service_desk_controller.rb @@ -3,6 +3,8 @@ class Projects::ServiceDeskController < Projects::ApplicationController before_action :authorize_admin_project! + feature_category :service_desk + def show json_response end diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 9a69ef991dd..93ad549bc50 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -12,13 +12,15 @@ class Projects::ServicesController < Projects::ApplicationController before_action :set_deprecation_notice_for_prometheus_service, only: [:edit, :update] before_action :redirect_deprecated_prometheus_service, only: [:update] before_action only: :edit do - push_frontend_feature_flag(:jira_issues_integration, @project, { default_enabled: true }) + push_frontend_feature_flag(:jira_issues_integration, @project, type: :licensed, default_enabled: true) end respond_to :html layout "project_settings" + feature_category :integrations + def edit @default_integration = Service.default_integration(service.type, project) end diff --git a/app/controllers/projects/settings/access_tokens_controller.rb b/app/controllers/projects/settings/access_tokens_controller.rb index d6b4c4dd5dc..cbd6716fdf7 100644 --- a/app/controllers/projects/settings/access_tokens_controller.rb +++ b/app/controllers/projects/settings/access_tokens_controller.rb @@ -7,6 +7,8 @@ module Projects before_action :check_feature_availability + feature_category :authentication_and_authorization + def index @project_access_token = PersonalAccessToken.new set_index_vars diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index d0d100fd88c..2963321f803 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -3,15 +3,25 @@ module Projects module Settings class CiCdController < Projects::ApplicationController + include RunnerSetupScripts + before_action :authorize_admin_pipeline! before_action :define_variables before_action do push_frontend_feature_flag(:new_variables_ui, @project, default_enabled: true) push_frontend_feature_flag(:ajax_new_deploy_token, @project) - push_frontend_feature_flag(:ci_key_autocomplete, default_enabled: true) end + helper_method :highlight_badge + + feature_category :continuous_integration + def show + if Feature.enabled?(:ci_pipeline_triggers_settings_vue_ui, @project) + @triggers_json = ::Ci::TriggerSerializer.new.represent( + @project.triggers, current_user: current_user, project: @project + ).to_json + end end def update @@ -48,8 +58,16 @@ module Projects redirect_to namespace_project_settings_ci_cd_path end + def runner_setup_scripts + private_runner_setup_scripts(project: @project) + end + private + def highlight_badge(name, content, language = nil) + Gitlab::Highlight.highlight(name, content, language: language) + end + def update_params params.require(:project).permit(*permitted_project_params) end @@ -58,9 +76,9 @@ module Projects [ :runners_token, :builds_enabled, :build_allow_git_fetch, :build_timeout_human_readable, :build_coverage_regex, :public_builds, - :auto_cancel_pending_pipelines, :forward_deployment_enabled, :ci_config_path, + :auto_cancel_pending_pipelines, :ci_config_path, auto_devops_attributes: [:id, :domain, :enabled, :deploy_strategy], - ci_cd_settings_attributes: [:default_git_depth] + ci_cd_settings_attributes: [:default_git_depth, :forward_deployment_enabled] ].tap do |list| list << :max_artifacts_size if can?(current_user, :update_max_artifacts_size, project) end @@ -116,6 +134,7 @@ module Projects def define_triggers_variables @triggers = @project.triggers .present(current_user: current_user) + @trigger = ::Ci::Trigger.new .present(current_user: current_user) end diff --git a/app/controllers/projects/settings/integrations_controller.rb b/app/controllers/projects/settings/integrations_controller.rb index 96bf7f474d1..fba11ff1497 100644 --- a/app/controllers/projects/settings/integrations_controller.rb +++ b/app/controllers/projects/settings/integrations_controller.rb @@ -6,6 +6,8 @@ module Projects before_action :authorize_admin_project! layout "project_settings" + feature_category :integrations + def show @services = @project.find_or_initialize_services end diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb index 781b850ddfe..c407b15e29f 100644 --- a/app/controllers/projects/settings/operations_controller.rb +++ b/app/controllers/projects/settings/operations_controller.rb @@ -9,6 +9,9 @@ module Projects respond_to :json, only: [:reset_alerting_token, :reset_pagerduty_token] helper_method :error_tracking_setting + helper_method :tracing_setting + + feature_category :incident_management def update result = ::Projects::Operations::UpdateService.new(project, current_user, update_params).execute @@ -17,15 +20,6 @@ module Projects render_update_response(result) end - # overridden in EE - def track_events(result) - if result[:status] == :success - ::Gitlab::Tracking::IncidentManagement.track_from_params( - update_params[:incident_management_setting_attributes] - ) - end - end - def reset_alerting_token result = ::Projects::Operations::UpdateService .new(project, current_user, alerting_params) @@ -55,6 +49,24 @@ module Projects private + def track_events(result) + if result[:status] == :success + ::Gitlab::Tracking::IncidentManagement.track_from_params( + update_params[:incident_management_setting_attributes] + ) + track_tracing_external_url + end + end + + def track_tracing_external_url + external_url_previous_change = project&.tracing_setting&.external_url_previous_change + + return unless external_url_previous_change + return unless external_url_previous_change[0].blank? && external_url_previous_change[1].present? + + ::Gitlab::Tracking.event('project:operations:tracing', 'external_url_populated') + end + def alerting_params { alerting_setting_attributes: { regenerate_token: true } } end @@ -106,6 +118,10 @@ module Projects project.build_error_tracking_setting end + def tracing_setting + @tracing_setting ||= project.tracing_setting || project.build_tracing_setting + end + def update_params params.require(:project).permit(permitted_project_params) end @@ -124,7 +140,8 @@ module Projects project: [:slug, :name, :organization_slug, :organization_name] ], - grafana_integration_attributes: [:token, :grafana_url, :enabled] + grafana_integration_attributes: [:token, :grafana_url, :enabled], + tracing_setting_attributes: [:external_url] } if Feature.enabled?(:settings_operations_prometheus_service, project) diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb index 35ca9336613..0994bebb1d0 100644 --- a/app/controllers/projects/settings/repository_controller.rb +++ b/app/controllers/projects/settings/repository_controller.rb @@ -7,8 +7,12 @@ module Projects before_action :define_variables, only: [:create_deploy_token] before_action do push_frontend_feature_flag(:ajax_new_deploy_token, @project) + push_frontend_feature_flag(:deploy_keys_on_protected_branches, @project) end + feature_category :source_code_management, [:show, :cleanup] + feature_category :continuous_delivery, [:create_deploy_token] + def show render_show end @@ -125,6 +129,7 @@ module Projects gon.push(protectable_tags_for_dropdown) gon.push(protectable_branches_for_dropdown) gon.push(access_levels_options) + gon.push(current_project_id: project.id) if project end end end diff --git a/app/controllers/projects/snippets/application_controller.rb b/app/controllers/projects/snippets/application_controller.rb index 3f488b07e96..8ee12bf3795 100644 --- a/app/controllers/projects/snippets/application_controller.rb +++ b/app/controllers/projects/snippets/application_controller.rb @@ -4,6 +4,8 @@ class Projects::Snippets::ApplicationController < Projects::ApplicationControlle include FindSnippet include SnippetAuthorizations + feature_category :snippets + private # This overrides the default snippet create authorization diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 632e8db9796..779e149bb9c 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -7,16 +7,11 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController before_action :check_snippets_available! - before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam] + before_action :snippet, only: [:show, :edit, :raw, :toggle_award_emoji, :mark_as_spam] - before_action :authorize_create_snippet!, only: [:new, :create] - before_action :authorize_read_snippet!, except: [:new, :create, :index] - before_action :authorize_update_snippet!, only: [:edit, :update] - before_action :authorize_admin_snippet!, only: [:destroy] - - before_action do - push_frontend_feature_flag(:snippet_multiple_files, current_user) - end + before_action :authorize_create_snippet!, only: :new + before_action :authorize_read_snippet!, except: [:new, :index] + before_action :authorize_update_snippet!, only: :edit def index @snippet_counts = ::Snippets::CountService @@ -37,14 +32,6 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController @snippet = @noteable = @project.snippets.build end - def create - create_params = snippet_params.merge(spammable_params) - service_response = ::Snippets::CreateService.new(project, current_user, create_params).execute - @snippet = service_response.payload[:snippet] - - handle_repository_error(:new) - end - protected alias_method :awardable, :snippet @@ -53,8 +40,4 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController def spammable_path project_snippet_path(@project, @snippet) end - - def snippet_params - params.require(:project_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description) - end end diff --git a/app/controllers/projects/starrers_controller.rb b/app/controllers/projects/starrers_controller.rb index d9654f4f72a..91f49fc4d66 100644 --- a/app/controllers/projects/starrers_controller.rb +++ b/app/controllers/projects/starrers_controller.rb @@ -3,6 +3,8 @@ class Projects::StarrersController < Projects::ApplicationController include SortingHelper + feature_category :projects + def index @starrers = UsersStarProjectsFinder.new(@project, params, current_user: @current_user).execute @sort = params[:sort].presence || sort_value_name diff --git a/app/controllers/projects/static_site_editor_controller.rb b/app/controllers/projects/static_site_editor_controller.rb index e97a8db0b79..7e2e32a843f 100644 --- a/app/controllers/projects/static_site_editor_controller.rb +++ b/app/controllers/projects/static_site_editor_controller.rb @@ -13,6 +13,8 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController push_frontend_feature_flag(:sse_image_uploads) end + feature_category :static_site_editor + def show service_response = ::StaticSiteEditor::ConfigService.new( container: project, @@ -25,14 +27,32 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController ).execute if service_response.success? - @data = service_response.payload + Gitlab::UsageDataCounters::StaticSiteEditorCounter.increment_views_count + + @data = serialize_necessary_payload_values_to_json(service_response.payload) else - respond_422 + # TODO: For now, if the service returns any error, the user is redirected + # to the root project page with the error message displayed as an alert. + # See https://gitlab.com/gitlab-org/gitlab/-/issues/213285#note_414808004 + # for discussion of plans to handle this via a page owned by the Static Site Editor. + flash[:alert] = service_response.message + redirect_to project_path(project) end end private + def serialize_necessary_payload_values_to_json(payload) + # This will convert booleans, Array-like and Hash-like objects to JSON + payload.transform_values do |value| + if value.is_a?(String) || value.is_a?(Integer) + value + else + value.to_json + end + end + end + def assign_ref_and_path @ref, @path = extract_ref(params.fetch(:id)) diff --git a/app/controllers/projects/tags/releases_controller.rb b/app/controllers/projects/tags/releases_controller.rb index c1f4cbce054..8e5539f546b 100644 --- a/app/controllers/projects/tags/releases_controller.rb +++ b/app/controllers/projects/tags/releases_controller.rb @@ -8,6 +8,8 @@ class Projects::Tags::ReleasesController < Projects::ApplicationController before_action :tag before_action :release + feature_category :release_evidence + def edit end diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index 475ca8894e4..1d783241196 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -10,6 +10,9 @@ class Projects::TagsController < Projects::ApplicationController before_action :authorize_download_code! before_action :authorize_admin_tag!, only: [:new, :create, :destroy] + feature_category :source_code_management, [:index, :show, :new, :destroy] + feature_category :release_evidence, [:create] + # rubocop: disable CodeReuse/ActiveRecord def index params[:sort] = params[:sort].presence || sort_value_recently_updated @@ -76,25 +79,10 @@ class Projects::TagsController < Projects::ApplicationController def destroy result = ::Tags::DestroyService.new(project, current_user).execute(params[:id]) - respond_to do |format| - if result[:status] == :success - format.html do - redirect_to project_tags_path(@project), status: :see_other - end - - format.js - else - @error = result[:message] - - format.html do - redirect_to project_tags_path(@project), - alert: @error, status: :see_other - end - - format.js do - render status: :ok - end - end + if result[:status] == :success + render json: result + else + render json: { message: result[:message] }, status: result[:return_code] end end diff --git a/app/controllers/projects/templates_controller.rb b/app/controllers/projects/templates_controller.rb index 95739f96d39..7ab23e39cf0 100644 --- a/app/controllers/projects/templates_controller.rb +++ b/app/controllers/projects/templates_controller.rb @@ -5,6 +5,8 @@ class Projects::TemplatesController < Projects::ApplicationController before_action :authorize_can_read_issuable! before_action :get_template_class + feature_category :templates + def show template = @template_type.find(params[:key], project) diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb index 33205b93317..6ba89ab34f8 100644 --- a/app/controllers/projects/todos_controller.rb +++ b/app/controllers/projects/todos_controller.rb @@ -6,6 +6,8 @@ class Projects::TodosController < Projects::ApplicationController before_action :authenticate_user!, only: [:create] + feature_category :issue_tracking + private def issuable diff --git a/app/controllers/projects/tracings_controller.rb b/app/controllers/projects/tracings_controller.rb new file mode 100644 index 00000000000..2bc0c590e8d --- /dev/null +++ b/app/controllers/projects/tracings_controller.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Projects + class TracingsController < Projects::ApplicationController + content_security_policy do |p| + next if p.directives.blank? + + global_frame_src = p.frame_src + + p.frame_src -> { frame_src_csp_policy(global_frame_src) } + end + + before_action :authorize_update_environment! + + feature_category :tracing + + def show + end + + private + + def frame_src_csp_policy(global_frame_src) + external_url = @project&.tracing_setting&.external_url + + external_url.presence || global_frame_src + end + end +end diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index 638e1a05c18..b5cfc3990b2 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -15,6 +15,8 @@ class Projects::TreeController < Projects::ApplicationController before_action :authorize_download_code! before_action :authorize_edit_tree!, only: [:create_dir] + feature_category :source_code_management + def show return render_404 unless @commit diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb index 7159d0243a3..eec35fcec8d 100644 --- a/app/controllers/projects/triggers_controller.rb +++ b/app/controllers/projects/triggers_controller.rb @@ -8,6 +8,8 @@ class Projects::TriggersController < Projects::ApplicationController layout 'project_settings' + feature_category :continuous_integration + def index redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers') end diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb index 72251988b5e..c15768e7bbb 100644 --- a/app/controllers/projects/uploads_controller.rb +++ b/app/controllers/projects/uploads_controller.rb @@ -11,6 +11,8 @@ class Projects::UploadsController < Projects::ApplicationController before_action :authorize_upload_file!, only: [:create, :authorize] before_action :verify_workhorse_api!, only: [:authorize] + feature_category :not_owned + private def upload_model_class diff --git a/app/controllers/projects/usage_ping_controller.rb b/app/controllers/projects/usage_ping_controller.rb index 0e2c8f879b1..9b4ddb326c1 100644 --- a/app/controllers/projects/usage_ping_controller.rb +++ b/app/controllers/projects/usage_ping_controller.rb @@ -3,6 +3,8 @@ class Projects::UsagePingController < Projects::ApplicationController before_action :authenticate_user! + feature_category :collection + def web_ide_clientside_preview return render_404 unless Gitlab::CurrentSettings.web_ide_clientside_preview_enabled? diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb index 0fd047f90cf..d8efc1b7b54 100644 --- a/app/controllers/projects/variables_controller.rb +++ b/app/controllers/projects/variables_controller.rb @@ -3,6 +3,8 @@ class Projects::VariablesController < Projects::ApplicationController before_action :authorize_admin_build! + feature_category :continuous_integration + def show respond_to do |format| format.json do diff --git a/app/controllers/projects/web_ide_schemas_controller.rb b/app/controllers/projects/web_ide_schemas_controller.rb index 3d16a6fafd4..84a191815f4 100644 --- a/app/controllers/projects/web_ide_schemas_controller.rb +++ b/app/controllers/projects/web_ide_schemas_controller.rb @@ -3,6 +3,8 @@ class Projects::WebIdeSchemasController < Projects::ApplicationController before_action :authenticate_user! + feature_category :web_ide + def show return respond_422 unless branch_sha diff --git a/app/controllers/projects/web_ide_terminals_controller.rb b/app/controllers/projects/web_ide_terminals_controller.rb index 76bcaa9e80c..1d179765ad9 100644 --- a/app/controllers/projects/web_ide_terminals_controller.rb +++ b/app/controllers/projects/web_ide_terminals_controller.rb @@ -8,6 +8,8 @@ class Projects::WebIdeTerminalsController < Projects::ApplicationController before_action :authorize_read_web_ide_terminal!, except: [:check_config, :create] before_action :authorize_update_web_ide_terminal!, only: [:cancel, :retry] + feature_category :web_ide + def check_config return respond_422 unless branch_sha diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index d0aa733cadb..8f794512486 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -5,6 +5,8 @@ class Projects::WikisController < Projects::ApplicationController alias_method :container, :project + feature_category :wiki + def git_access end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 848625ff6b5..09e7563cefd 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -37,7 +37,7 @@ class ProjectsController < Projects::ApplicationController # Experiments before_action only: [:new, :create] do frontend_experimentation_tracking_data(:new_create_project_ui, 'click_tab') - push_frontend_feature_flag(:new_create_project_ui) if experiment_enabled?(:new_create_project_ui) + push_frontend_experiment(:new_create_project_ui) end before_action only: [:edit] do @@ -47,6 +47,17 @@ class ProjectsController < Projects::ApplicationController layout :determine_layout + feature_category :projects, [ + :index, :show, :new, :create, :edit, :update, :transfer, + :destroy, :resolve, :archive, :unarchive, :toggle_star + ] + + feature_category :source_code_management, [:remove_fork, :housekeeping, :refs] + feature_category :issue_tracking, [:preview_markdown, :new_issuable_address] + feature_category :importers, [:export, :remove_export, :generate_new_export, :download_export] + feature_category :audit_events, [:activity] + feature_category :code_review, [:unfoldered_environment_names] + def index redirect_to(current_user ? root_path : explore_root_path) end @@ -305,6 +316,16 @@ class ProjectsController < Projects::ApplicationController end end + def unfoldered_environment_names + return render_404 unless Feature.enabled?(:deployment_filters) + + respond_to do |format| + format.json do + render json: EnvironmentNamesFinder.new(@project, current_user).execute + end + end + end + private # Render project landing depending of which features are available diff --git a/app/controllers/registrations/experience_levels_controller.rb b/app/controllers/registrations/experience_levels_controller.rb index 38cffff91eb..23126983eb5 100644 --- a/app/controllers/registrations/experience_levels_controller.rb +++ b/app/controllers/registrations/experience_levels_controller.rb @@ -7,12 +7,20 @@ module Registrations before_action :check_experiment_enabled before_action :ensure_namespace_path_param + feature_category :navigation + def update current_user.experience_level = params[:experience_level] if current_user.save hide_advanced_issues - redirect_to group_path(params[:namespace_path]) + record_experiment_user(:default_to_issues_board) + + if experiment_enabled?(:default_to_issues_board) && learn_gitlab.available? + redirect_to namespace_project_board_path(params[:namespace_path], learn_gitlab.project, learn_gitlab.board) + else + redirect_to group_path(params[:namespace_path]) + end else render :show end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 204520a3e71..b3dc0e986f4 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -6,19 +6,19 @@ class RegistrationsController < Devise::RegistrationsController include RecaptchaExperimentHelper include InvisibleCaptchaOnSignup + BLOCKED_PENDING_APPROVAL_STATE = 'blocked_pending_approval'.freeze + layout :choose_layout skip_before_action :required_signup_info, :check_two_factor_requirement, only: [:welcome, :update_registration] prepend_before_action :check_captcha, only: :create before_action :whitelist_query_limiting, :ensure_destroy_prerequisites_met, only: [:destroy] - before_action :ensure_terms_accepted, - if: -> { action_name == 'create' && Gitlab::CurrentSettings.current_application_settings.enforce_terms? } before_action :load_recaptcha, only: :new + feature_category :authentication_and_authorization + def new if experiment_enabled?(:signup_flow) - track_experiment_event(:terms_opt_in, 'start') - @resource = build_resource else redirect_to new_user_session_path(anchor: 'register-pane') @@ -26,18 +26,18 @@ class RegistrationsController < Devise::RegistrationsController end def create + set_user_state accept_pending_invitations super do |new_user| persist_accepted_terms_if_required(new_user) set_role_required(new_user) - track_terms_experiment(new_user) yield new_user if block_given? end - # Devise sets a flash message on `create` for a successful signup, - # which we don't want to show. - flash[:notice] = nil + # Devise sets a flash message on both successful & failed signups, + # but we only want to show a message if the resource is blocked by a pending approval. + flash[:notice] = nil unless resource.blocked_pending_approval? rescue Gitlab::Access::AccessDeniedError redirect_to(new_user_session_path) end @@ -58,6 +58,8 @@ class RegistrationsController < Devise::RegistrationsController end def update_registration + return redirect_to new_user_registration_path unless current_user + user_params = params.require(:user).permit(:role, :setup_for_company) result = ::Users::SignupService.new(current_user, user_params).execute @@ -81,10 +83,8 @@ class RegistrationsController < Devise::RegistrationsController return unless new_user.persisted? return unless Gitlab::CurrentSettings.current_application_settings.enforce_terms? - if terms_accepted? - terms = ApplicationSetting::Term.latest - Users::RespondToTermsService.new(new_user, terms).execute(accepted: true) - end + terms = ApplicationSetting::Term.latest + Users::RespondToTermsService.new(new_user, terms).execute(accepted: true) end def set_role_required(new_user) @@ -119,6 +119,8 @@ 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? + Feature.enabled?(:soft_email_confirmation) ? dashboard_projects_path : users_almost_there_path end @@ -178,18 +180,6 @@ class RegistrationsController < Devise::RegistrationsController Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42380') end - def ensure_terms_accepted - return if terms_accepted? - - redirect_to new_user_session_path, alert: _('You must accept our Terms of Service and privacy policy in order to register an account') - end - - def terms_accepted? - return true if experiment_enabled?(:terms_opt_in) - - Gitlab::Utils.to_boolean(params[:terms_opt_in]) - end - def path_for_signed_in_user(user) if requires_confirmation?(user) users_almost_there_path @@ -206,13 +196,6 @@ class RegistrationsController < Devise::RegistrationsController true end - def track_terms_experiment(new_user) - return unless new_user.persisted? - - track_experiment_event(:terms_opt_in, 'end') - record_experiment_user(:terms_opt_in) - end - def load_recaptcha Gitlab::Recaptcha.load_configurations! end @@ -233,6 +216,13 @@ class RegistrationsController < Devise::RegistrationsController !helpers.in_oauth_flow? && !helpers.in_trial_flow? end + + def set_user_state + return unless Feature.enabled?(:admin_approval_for_new_user_signups, default_enabled: true) + return unless Gitlab::CurrentSettings.require_admin_approval_after_user_signup + + resource.state = BLOCKED_PENDING_APPROVAL_STATE + end end RegistrationsController.prepend_if_ee('EE::RegistrationsController') diff --git a/app/controllers/repositories/git_http_client_controller.rb b/app/controllers/repositories/git_http_client_controller.rb index e02955433b2..de452aa69b7 100644 --- a/app/controllers/repositories/git_http_client_controller.rb +++ b/app/controllers/repositories/git_http_client_controller.rb @@ -20,6 +20,8 @@ module Repositories prepend_before_action :authenticate_user, :parse_repo_path + feature_category :source_code_management + private def download_request? diff --git a/app/controllers/runner_setup_controller.rb b/app/controllers/runner_setup_controller.rb new file mode 100644 index 00000000000..e0e9c5b7c23 --- /dev/null +++ b/app/controllers/runner_setup_controller.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class RunnerSetupController < ApplicationController + feature_category :continuous_integration + + def platforms + render json: Gitlab::Ci::RunnerInstructions::OS.merge(Gitlab::Ci::RunnerInstructions::OTHER_ENVIRONMENTS) + end +end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index dedaf0c903a..0380bc1c548 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -8,7 +8,8 @@ class SearchController < ApplicationController SCOPE_PRELOAD_METHOD = { projects: :with_web_entity_associations, - issues: :with_web_entity_associations + issues: :with_web_entity_associations, + epics: :with_web_entity_associations }.freeze track_redis_hll_event :show, name: 'i_search_total', feature: :search_track_unique_users, feature_default_enabled: true @@ -24,6 +25,8 @@ class SearchController < ApplicationController layout 'search' + feature_category :global_search + def show @project = search_service.project @group = search_service.group @@ -38,6 +41,7 @@ class SearchController < ApplicationController @show_snippets = search_service.show_snippets? @search_results = search_service.search_results @search_objects = search_service.search_objects(preload_method) + @search_highlight = search_service.search_highlight render_commits if @scope == 'commits' eager_load_user_status if @scope == 'users' @@ -96,8 +100,6 @@ class SearchController < ApplicationController end def eager_load_user_status - return if Feature.disabled?(:users_search, default_enabled: true) - @search_objects = @search_objects.eager_load(:status) # rubocop:disable CodeReuse/ActiveRecord end diff --git a/app/controllers/sent_notifications_controller.rb b/app/controllers/sent_notifications_controller.rb index 20134de81a0..db07b212d00 100644 --- a/app/controllers/sent_notifications_controller.rb +++ b/app/controllers/sent_notifications_controller.rb @@ -3,6 +3,8 @@ class SentNotificationsController < ApplicationController skip_before_action :authenticate_user! + feature_category :users + def unsubscribe @sent_notification = SentNotification.for(params[:id]) diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 318553b5e0a..61120c5b7d1 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -49,6 +49,8 @@ class SessionsController < Devise::SessionsController # token mismatch. protect_from_forgery with: :exception, prepend: true, except: :destroy + feature_category :authentication_and_authorization + CAPTCHA_HEADER = 'X-GitLab-Show-Login-Captcha' MAX_FAILED_LOGIN_ATTEMPTS = 5 @@ -262,8 +264,11 @@ class SessionsController < Devise::SessionsController end def valid_otp_attempt?(user) - user.validate_and_consume_otp!(user_params[:otp_attempt]) || - user.invalidate_otp_backup_code!(user_params[:otp_attempt]) + otp_validation_result = + ::Users::ValidateOtpService.new(user).execute(user_params[:otp_attempt]) + return true if otp_validation_result[:status] == :success + + user.invalidate_otp_backup_code!(user_params[:otp_attempt]) end def log_audit_event(user, resource, options = {}) diff --git a/app/controllers/snippets/application_controller.rb b/app/controllers/snippets/application_controller.rb index a533e46a75d..f259f4569ef 100644 --- a/app/controllers/snippets/application_controller.rb +++ b/app/controllers/snippets/application_controller.rb @@ -4,6 +4,8 @@ class Snippets::ApplicationController < ApplicationController include FindSnippet include SnippetAuthorizations + feature_category :snippets + private def authorize_read_snippet! diff --git a/app/controllers/snippets/notes_controller.rb b/app/controllers/snippets/notes_controller.rb index a7e8ef0798b..8532257cb8d 100644 --- a/app/controllers/snippets/notes_controller.rb +++ b/app/controllers/snippets/notes_controller.rb @@ -8,6 +8,8 @@ class Snippets::NotesController < ApplicationController before_action :authorize_read_snippet!, only: [:show, :index] before_action :authorize_create_note!, only: [:create] + feature_category :snippets + private def note diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 486c7f1d028..913b1e3bb6e 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -6,21 +6,16 @@ class SnippetsController < Snippets::ApplicationController include ToggleAwardEmoji include SpammableActions - before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam] + before_action :snippet, only: [:show, :edit, :raw, :toggle_award_emoji, :mark_as_spam] - before_action :authorize_create_snippet!, only: [:new, :create] + before_action :authorize_create_snippet!, only: :new before_action :authorize_read_snippet!, only: [:show, :raw] - before_action :authorize_update_snippet!, only: [:edit, :update] - before_action :authorize_admin_snippet!, only: [:destroy] + before_action :authorize_update_snippet!, only: :edit skip_before_action :authenticate_user!, only: [:index, :show, :raw] layout 'snippets' - before_action do - push_frontend_feature_flag(:snippet_multiple_files, current_user) - end - def index if params[:username].present? @user = UserFinder.new(params[:username]).find_by_username! @@ -44,18 +39,6 @@ class SnippetsController < Snippets::ApplicationController @snippet = PersonalSnippet.new end - def create - create_params = snippet_params.merge(files: params.delete(:files)) - service_response = Snippets::CreateService.new(nil, current_user, create_params).execute - @snippet = service_response.payload[:snippet] - - if service_response.error? && @snippet.errors[:repository].present? - handle_repository_error(:new) - else - recaptcha_check_with_fallback { render :new } - end - end - protected alias_method :awardable, :snippet @@ -64,8 +47,4 @@ class SnippetsController < Snippets::ApplicationController def spammable_path snippet_path(@snippet) end - - def snippet_params - params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description).merge(spammable_params) - end end diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index 9510734bc9b..6692c285335 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -25,6 +25,8 @@ class UploadsController < ApplicationController before_action :authorize_create_access!, only: [:create, :authorize] before_action :verify_workhorse_api!, only: [:authorize] + feature_category :not_owned + def uploader_class PersonalFileUploader end diff --git a/app/controllers/user_callouts_controller.rb b/app/controllers/user_callouts_controller.rb index 06f422b9d90..cfec9d6d905 100644 --- a/app/controllers/user_callouts_controller.rb +++ b/app/controllers/user_callouts_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class UserCalloutsController < ApplicationController + feature_category :navigation + def create callout = ensure_callout diff --git a/app/controllers/users/terms_controller.rb b/app/controllers/users/terms_controller.rb index 231e449f733..be670658048 100644 --- a/app/controllers/users/terms_controller.rb +++ b/app/controllers/users/terms_controller.rb @@ -14,6 +14,8 @@ module Users layout 'terms' + feature_category :users + def index @redirect = redirect_path diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 75a861423ed..672f36dedc0 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -21,6 +21,8 @@ class UsersController < ApplicationController before_action :authorize_read_user_profile!, only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :starred_projects, :snippets] + feature_category :users + def show respond_to do |format| format.html @@ -106,7 +108,7 @@ class UsersController < ApplicationController def calendar_activities @calendar_date = Date.parse(params[:date]) rescue Date.today - @events = contributions_calendar.events_by_date(@calendar_date) + @events = contributions_calendar.events_by_date(@calendar_date).map(&:present) render 'calendar_activities', layout: false end diff --git a/app/controllers/whats_new_controller.rb b/app/controllers/whats_new_controller.rb new file mode 100644 index 00000000000..7156faa4e49 --- /dev/null +++ b/app/controllers/whats_new_controller.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class WhatsNewController < ApplicationController + include Gitlab::WhatsNew + + skip_before_action :authenticate_user! + + before_action :check_feature_flag + + feature_category :navigation + + def index + respond_to do |format| + format.js do + render json: whats_new_most_recent_release_items + end + end + end + + private + + def check_feature_flag + render_404 unless Feature.enabled?(:whats_new_drawer, current_user) + end +end |