diff options
Diffstat (limited to 'app/controllers')
89 files changed, 590 insertions, 391 deletions
diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index eec56682300..f6b4fbac8d5 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -19,6 +19,13 @@ class AbuseReportsController < ApplicationController reported_from_url: report_params[:reported_from_url] ) + Gitlab::Tracking.event( + 'ReportAbuse', + 'select_abuse_category', + property: report_params[:category], + user: @user + ) + render :new end @@ -29,6 +36,13 @@ class AbuseReportsController < ApplicationController if @abuse_report.save @abuse_report.notify + Gitlab::Tracking.event( + 'ReportAbuse', + 'submit_form', + property: @abuse_report.category, + user: @abuse_report.user + ) + message = _("Thank you for your report. A GitLab administrator will look into it shortly.") redirect_to root_path, notice: message elsif report_params[:user_id].present? @@ -41,7 +55,7 @@ class AbuseReportsController < ApplicationController private def report_params - params.require(:abuse_report).permit(:message, :user_id, :category, :reported_from_url) + params.require(:abuse_report).permit(:message, :user_id, :category, :reported_from_url, links_to_spam: []) end # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/controllers/admin/application_settings/appearances_controller.rb b/app/controllers/admin/application_settings/appearances_controller.rb index 3cb31ff756f..719e8e4a913 100644 --- a/app/controllers/admin/application_settings/appearances_controller.rb +++ b/app/controllers/admin/application_settings/appearances_controller.rb @@ -46,6 +46,15 @@ class Admin::ApplicationSettings::AppearancesController < Admin::ApplicationCont redirect_to admin_application_settings_appearances_path, notice: _('Header logo was successfully removed.') end + def pwa_icon + @appearance.remove_pwa_icon! + + @appearance.save + + redirect_to admin_application_settings_appearances_path, + notice: _('Progressive Web App (PWA) icon was successfully removed.') + end + def favicon @appearance.remove_favicon! @appearance.save @@ -68,12 +77,16 @@ class Admin::ApplicationSettings::AppearancesController < Admin::ApplicationCont def allowed_appearance_params %i[ title - pwa_short_name description + pwa_name + pwa_short_name + pwa_description logo logo_cache header_logo header_logo_cache + pwa_icon + pwa_icon_cache favicon favicon_cache new_project_guidelines diff --git a/app/controllers/admin/jobs_controller.rb b/app/controllers/admin/jobs_controller.rb index ef9264d1615..5ea8c672993 100644 --- a/app/controllers/admin/jobs_controller.rb +++ b/app/controllers/admin/jobs_controller.rb @@ -6,6 +6,10 @@ class Admin::JobsController < Admin::ApplicationController feature_category :continuous_integration urgency :low + before_action do + push_frontend_feature_flag(:admin_jobs_vue) + end + 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/runners_controller.rb b/app/controllers/admin/runners_controller.rb index 96fe0c9331d..21a3a0aea0b 100644 --- a/app/controllers/admin/runners_controller.rb +++ b/app/controllers/admin/runners_controller.rb @@ -3,7 +3,11 @@ class Admin::RunnersController < Admin::ApplicationController include RunnerSetupScripts - before_action :runner, except: [:index, :tag_list, :runner_setup_scripts] + before_action :runner, except: [:index, :new, :tag_list, :runner_setup_scripts] + + before_action only: [:index] do + push_frontend_feature_flag(:create_runner_workflow, current_user) + end feature_category :runner urgency :low @@ -18,6 +22,10 @@ class Admin::RunnersController < Admin::ApplicationController assign_projects end + def new + render_404 unless Feature.enabled?(:create_runner_workflow, current_user) + end + def update if Ci::Runners::UpdateRunnerService.new(@runner).execute(runner_params).success? respond_to do |format| @@ -29,28 +37,6 @@ class Admin::RunnersController < Admin::ApplicationController end end - def destroy - Ci::Runners::UnregisterRunnerService.new(@runner, current_user).execute - - redirect_to admin_runners_path, status: :found - end - - def resume - if Ci::Runners::UpdateRunnerService.new(@runner).execute(active: true).success? - redirect_to admin_runners_path, notice: _('Runner was successfully updated.') - else - redirect_to admin_runners_path, alert: _('Runner was not updated.') - end - end - - def pause - if Ci::Runners::UpdateRunnerService.new(@runner).execute(active: false).success? - redirect_to admin_runners_path, notice: _('Runner was successfully updated.') - else - redirect_to admin_runners_path, alert: _('Runner was not updated.') - end - end - def tag_list tags = Autocomplete::ActsAsTaggableOn::TagsFinder.new(params: params).execute diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 4f379d8a75b..00b17bf381f 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -7,7 +7,6 @@ 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_ban_user_feature_flag, only: [:ban] feature_category :user_management @@ -359,6 +358,7 @@ class Admin::UsersController < Admin::ApplicationController :skype, :theme_id, :twitter, + :discord, :username, :website_url, :note, @@ -377,10 +377,6 @@ class Admin::UsersController < Admin::ApplicationController access_denied! unless Gitlab.config.gitlab.impersonation_enabled end - def check_ban_user_feature_flag - access_denied! unless Feature.enabled?(:ban_user_feature_flag) - end - def log_impersonation_event Gitlab::AppLogger.info(format(_("User %{current_user_username} has started impersonating %{username}"), current_user_username: current_user.username, username: user.username)) end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 36aae42e21f..353f9098b95 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -69,7 +69,7 @@ class ApplicationController < ActionController::Base :masked_page_url def self.endpoint_id_for_action(action_name) - "#{self.name}##{action_name}" + "#{name}##{action_name}" end rescue_from Encoding::CompatibilityError do |exception| @@ -510,8 +510,6 @@ class ApplicationController < ActionController::Base end def set_locale(&block) - return Gitlab::I18n.with_user_locale(current_user, &block) unless Feature.enabled?(:preferred_language_switcher) - if current_user Gitlab::I18n.with_user_locale(current_user, &block) else diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 668b2ebaf9e..01cc1ef21c6 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -6,7 +6,7 @@ class AutocompleteController < ApplicationController skip_before_action :authenticate_user!, only: [:users, :award_emojis, :merge_request_target_branches] before_action :check_search_rate_limit!, only: [:users, :projects] - feature_category :users, [:users, :user] + feature_category :user_profile, [:users, :user] feature_category :projects, [:projects] feature_category :team_planning, [:award_emojis] feature_category :code_review_workflow, [:merge_request_target_branches] diff --git a/app/controllers/concerns/analytics/cycle_analytics/stage_actions.rb b/app/controllers/concerns/analytics/cycle_analytics/stage_actions.rb index eebc40f33f4..b0220b17cf9 100644 --- a/app/controllers/concerns/analytics/cycle_analytics/stage_actions.rb +++ b/app/controllers/concerns/analytics/cycle_analytics/stage_actions.rb @@ -7,9 +7,11 @@ module Analytics extend ActiveSupport::Concern included do + extend ::Gitlab::Utils::Override include CycleAnalyticsParams - before_action :validate_params, only: %i[median] + before_action :validate_params, except: %i[index] + before_action :authorize_stage, except: %i[index] end def index @@ -44,11 +46,11 @@ module Analytics private - def parent + def namespace raise NotImplementedError end - def value_stream_class + def authorize_stage raise NotImplementedError end @@ -64,7 +66,7 @@ module Analytics end def stage - @stage ||= ::Analytics::CycleAnalytics::StageFinder.new(parent: parent, stage_id: params[:id]).execute + @stage ||= ::Analytics::CycleAnalytics::StageFinder.new(parent: namespace, stage_id: params[:id]).execute end def data_collector @@ -75,7 +77,7 @@ module Analytics end def value_stream - @value_stream ||= value_stream_class.build_default_value_stream(parent) + @value_stream ||= Analytics::CycleAnalytics::ValueStream.build_default_value_stream(namespace) end def list_params @@ -83,7 +85,7 @@ module Analytics end def list_service - Analytics::CycleAnalytics::Stages::ListService.new(parent: parent, current_user: current_user, params: list_params) + Analytics::CycleAnalytics::Stages::ListService.new(parent: namespace, current_user: current_user, params: list_params) end def cycle_analytics_configuration(stages) @@ -94,3 +96,5 @@ module Analytics end end end + +Analytics::CycleAnalytics::StageActions.prepend_mod_with('Analytics::CycleAnalytics::StageActions') diff --git a/app/controllers/concerns/analytics/cycle_analytics/value_stream_actions.rb b/app/controllers/concerns/analytics/cycle_analytics/value_stream_actions.rb new file mode 100644 index 00000000000..f10b23d1664 --- /dev/null +++ b/app/controllers/concerns/analytics/cycle_analytics/value_stream_actions.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Analytics + module CycleAnalytics + module ValueStreamActions + extend ActiveSupport::Concern + + included do + before_action :authorize + end + + def index + # FOSS users can only see the default value stream + value_streams = [Analytics::CycleAnalytics::ValueStream.build_default_value_stream(namespace)] + + render json: Analytics::CycleAnalytics::ValueStreamSerializer.new.represent(value_streams) + end + + private + + def namespace + raise NotImplementedError + end + + def authorize + authorize_read_cycle_analytics! + end + end + end +end + +Analytics::CycleAnalytics::ValueStreamActions.prepend_mod_with('Analytics::CycleAnalytics::ValueStreamActions') diff --git a/app/controllers/concerns/ci/auth_build_trace.rb b/app/controllers/concerns/ci/auth_build_trace.rb new file mode 100644 index 00000000000..0370a382eb8 --- /dev/null +++ b/app/controllers/concerns/ci/auth_build_trace.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Ci + module AuthBuildTrace + extend ActiveSupport::Concern + + def authorize_read_build_trace! + return if can?(current_user, :read_build_trace, build) + + if build.debug_mode? + access_denied!( + _('You must have developer or higher permissions in the associated project to view job logs when debug ' \ + "trace is enabled. To disable debug trace, set the 'CI_DEBUG_TRACE' and 'CI_DEBUG_SERVICES' variables to " \ + "'false' in your pipeline configuration or CI/CD settings. If you must view this job log, " \ + 'a project maintainer or owner must add you to the project with developer permissions or higher.') + ) + else + access_denied!(_('The current user is not authorized to access the job log.')) + end + end + end +end diff --git a/app/controllers/concerns/clientside_preview_csp.rb b/app/controllers/concerns/clientside_preview_csp.rb deleted file mode 100644 index 6892c441b67..00000000000 --- a/app/controllers/concerns/clientside_preview_csp.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -module ClientsidePreviewCSP - extend ActiveSupport::Concern - - included do - content_security_policy do |p| - next if p.directives.blank? - next unless Gitlab::CurrentSettings.web_ide_clientside_preview_enabled? - - default_frame_src = p.directives['frame-src'] || p.directives['default-src'] - frame_src_values = Array.wrap(default_frame_src) | [Gitlab::CurrentSettings.web_ide_clientside_preview_bundler_url].compact - - p.frame_src(*frame_src_values) - end - end -end diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb index 0669f051457..e1381b4173f 100644 --- a/app/controllers/concerns/issuable_actions.rb +++ b/app/controllers/concerns/issuable_actions.rb @@ -90,7 +90,7 @@ module IssuableActions end def destroy - Issuable::DestroyService.new(project: issuable.project, current_user: current_user).execute(issuable) + Issuable::DestroyService.new(container: issuable.project, current_user: current_user).execute(issuable) name = issuable.human_class_name flash[:notice] = "The #{name} was successfully deleted." @@ -246,7 +246,21 @@ module IssuableActions end def bulk_update_params - params.require(:update).permit(bulk_update_permitted_keys) + clean_bulk_update_params( + params.require(:update).permit(bulk_update_permitted_keys) + ) + end + + def clean_bulk_update_params(permitted_params) + permitted_params.delete_if do |k, v| + next if k == :issuable_ids + + if v.is_a?(Array) + v.compact.empty? + else + v.blank? + end + end end def bulk_update_permitted_keys @@ -254,7 +268,6 @@ module IssuableActions :issuable_ids, :assignee_id, :milestone_id, - :sprint_id, :state_event, :subscription_event, assignee_ids: [], diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb index 5060ce69d9c..a202808e2c3 100644 --- a/app/controllers/concerns/issuable_collections.rb +++ b/app/controllers/concerns/issuable_collections.rb @@ -14,18 +14,8 @@ module IssuableCollections private - def show_alert_if_search_is_disabled - if current_user || params[:search].blank? || !html_request? || Feature.disabled?(:disable_anonymous_search, type: :ops) - return - end - - flash.now[:notice] = _('You must sign in to search for specific terms.') - end - # rubocop:disable Gitlab/ModuleWithInstanceVariables def set_issuables_index - show_alert_if_search_is_disabled - @issuables = issuables_collection set_pagination diff --git a/app/controllers/concerns/issuable_collections_action.rb b/app/controllers/concerns/issuable_collections_action.rb index b8249345a54..31445eb3eca 100644 --- a/app/controllers/concerns/issuable_collections_action.rb +++ b/app/controllers/concerns/issuable_collections_action.rb @@ -7,14 +7,12 @@ module IssuableCollectionsAction included do before_action :check_search_rate_limit!, only: [:issues, :merge_requests], if: -> { - params[:search].present? && Feature.enabled?(:rate_limit_issuable_searches) + params[:search].present? } end # rubocop:disable Gitlab/ModuleWithInstanceVariables def issues - show_alert_if_search_is_disabled - @issues = issuables_collection .non_archived .page(params[:page]) @@ -28,11 +26,13 @@ module IssuableCollectionsAction end def merge_requests - show_alert_if_search_is_disabled - @merge_requests = issuables_collection.page(params[:page]) @issuable_meta_data = Gitlab::IssuableMetadata.new(current_user, @merge_requests).data + rescue ActiveRecord::QueryCanceled => exception # rubocop:disable Database/RescueQueryCanceled + log_exception(exception) + + @search_timeout_occurred = true end # rubocop:enable Gitlab/ModuleWithInstanceVariables diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb index 7c6e449b509..773e4c15d6e 100644 --- a/app/controllers/concerns/membership_actions.rb +++ b/app/controllers/concerns/membership_actions.rb @@ -6,7 +6,7 @@ module MembershipActions def update update_params = params.require(root_params_key).permit(:access_level, :expires_at) - member = membershipable.members_and_requesters.find(params[:id]) + member = members_and_requesters.find(params[:id]) result = Members::UpdateService .new(current_user, update_params) .execute(member) @@ -30,7 +30,7 @@ module MembershipActions end def destroy - member = membershipable.members_and_requesters.find(params[:id]) + member = members_and_requesters.find(params[:id]) skip_subresources = !ActiveRecord::Type::Boolean.new.cast(params.delete(:remove_sub_memberships)) # !! is used in case unassign_issuables contains empty string which would result in nil unassign_issuables = !!ActiveRecord::Type::Boolean.new.cast(params.delete(:unassign_issuables)) @@ -71,7 +71,7 @@ module MembershipActions end def approve_access_request - access_requester = membershipable.requesters.find(params[:id]) + access_requester = requesters.find(params[:id]) Members::ApproveAccessRequestService .new(current_user, params) .execute(access_requester) @@ -81,7 +81,7 @@ module MembershipActions # rubocop: disable CodeReuse/ActiveRecord def leave - member = membershipable.members_and_requesters.find_by!(user_id: current_user.id) + member = members_and_requesters.find_by!(user_id: current_user.id) Members::DestroyService.new(current_user).execute(member) notice = @@ -140,6 +140,14 @@ module MembershipActions raise NotImplementedError end + def members_and_requesters + membershipable.members_and_requesters + end + + def requesters + membershipable.requesters + end + def requested_relations(inherited_permissions = :with_inherited_permissions) case params[inherited_permissions].presence when 'exclude' diff --git a/app/controllers/concerns/metrics_dashboard.rb b/app/controllers/concerns/metrics_dashboard.rb index d4e8e95e016..338c3af235b 100644 --- a/app/controllers/concerns/metrics_dashboard.rb +++ b/app/controllers/concerns/metrics_dashboard.rb @@ -37,7 +37,7 @@ module MetricsDashboard def all_dashboards dashboard_finder .find_all_paths(project_for_dashboard) - .map(&method(:amend_dashboard)) + .map { |dashboard| amend_dashboard(dashboard) } end def amend_dashboard(dashboard) diff --git a/app/controllers/concerns/preferred_language_switcher.rb b/app/controllers/concerns/preferred_language_switcher.rb index 00cd0f9d1d5..872652100c9 100644 --- a/app/controllers/concerns/preferred_language_switcher.rb +++ b/app/controllers/concerns/preferred_language_switcher.rb @@ -6,8 +6,6 @@ module PreferredLanguageSwitcher private def init_preferred_language - return unless Feature.enabled?(:preferred_language_switcher) - cookies[:preferred_language] = preferred_language end diff --git a/app/controllers/concerns/product_analytics_tracking.rb b/app/controllers/concerns/product_analytics_tracking.rb index b01320ce3ec..5696e441ad0 100644 --- a/app/controllers/concerns/product_analytics_tracking.rb +++ b/app/controllers/concerns/product_analytics_tracking.rb @@ -5,6 +5,8 @@ module ProductAnalyticsTracking include RedisTracking extend ActiveSupport::Concern + MIGRATED_EVENTS = ['g_analytics_valuestream'].freeze + class_methods do # TODO: Remove once all the events are migrated to #track_custom_event # during https://gitlab.com/groups/gitlab-org/-/epics/8641 @@ -63,9 +65,9 @@ module ProductAnalyticsTracking end def event_enabled?(event) - events_to_ff = { - g_analytics_valuestream: '', + return true if MIGRATED_EVENTS.include?(event) + events_to_ff = { i_search_paid: :_phase2, i_search_total: :_phase2, i_search_advanced: :_phase2, diff --git a/app/controllers/concerns/record_user_last_activity.rb b/app/controllers/concerns/record_user_last_activity.rb index 6ac87d8f27b..501590d33d9 100644 --- a/app/controllers/concerns/record_user_last_activity.rb +++ b/app/controllers/concerns/record_user_last_activity.rb @@ -20,6 +20,7 @@ module RecordUserLastActivity return if Gitlab::Database.read_only? return unless current_user && current_user.last_activity_on != Date.today - Users::ActivityService.new(current_user).execute + # TODO: add namespace & project - https://gitlab.com/gitlab-org/gitlab/-/issues/387952 + Users::ActivityService.new(author: current_user).execute end end diff --git a/app/controllers/concerns/requires_whitelisted_monitoring_client.rb b/app/controllers/concerns/requires_whitelisted_monitoring_client.rb index e98c1a30887..ef3d281589a 100644 --- a/app/controllers/concerns/requires_whitelisted_monitoring_client.rb +++ b/app/controllers/concerns/requires_whitelisted_monitoring_client.rb @@ -22,7 +22,7 @@ module RequiresWhitelistedMonitoringClient end def ip_whitelist - @ip_whitelist ||= Settings.monitoring.ip_whitelist.map(&IPAddr.method(:new)) + @ip_whitelist ||= Settings.monitoring.ip_whitelist.map { |ip| IPAddr.new(ip) } end def valid_token? diff --git a/app/controllers/concerns/send_file_upload.rb b/app/controllers/concerns/send_file_upload.rb index c91edb74d6b..2141b257b40 100644 --- a/app/controllers/concerns/send_file_upload.rb +++ b/app/controllers/concerns/send_file_upload.rb @@ -63,21 +63,32 @@ module SendFileUpload private def image_scaling_request?(file_upload) - avatar_safe_for_scaling?(file_upload) && - scaling_allowed_by_feature_flags?(file_upload) && - valid_image_scaling_width? + (avatar_safe_for_scaling?(file_upload) || pwa_icon_safe_for_scaling?(file_upload)) && + scaling_allowed_by_feature_flags?(file_upload) + end + + def pwa_icon_safe_for_scaling?(file_upload) + file_upload.try(:image_safe_for_scaling?) && + mounted_as_pwa_icon?(file_upload) && + valid_image_scaling_width?(Appearance::ALLOWED_PWA_ICON_SCALER_WIDTHS) end def avatar_safe_for_scaling?(file_upload) - file_upload.try(:image_safe_for_scaling?) && mounted_as_avatar?(file_upload) + file_upload.try(:image_safe_for_scaling?) && + mounted_as_avatar?(file_upload) && + valid_image_scaling_width?(Avatarable::ALLOWED_IMAGE_SCALER_WIDTHS) end def mounted_as_avatar?(file_upload) file_upload.try(:mounted_as)&.to_sym == :avatar end - def valid_image_scaling_width? - Avatarable::ALLOWED_IMAGE_SCALER_WIDTHS.include?(params[:width]&.to_i) + def mounted_as_pwa_icon?(file_upload) + file_upload.try(:mounted_as)&.to_sym == :pwa_icon + end + + def valid_image_scaling_width?(allowed_scalar_widths) + allowed_scalar_widths.include?(params[:width]&.to_i) end def scaling_allowed_by_feature_flags?(file_upload) diff --git a/app/controllers/concerns/sends_blob.rb b/app/controllers/concerns/sends_blob.rb index 3cf260c9f1b..3303b704ebe 100644 --- a/app/controllers/concerns/sends_blob.rb +++ b/app/controllers/concerns/sends_blob.rb @@ -27,12 +27,7 @@ module SendsBlob private def cached_blob?(blob, allow_caching: false) - stale = - if Feature.enabled?(:improve_blobs_cache_headers) - stale?(strong_etag: blob.id) - else - stale?(etag: blob.id) - end + stale = stale?(strong_etag: blob.id) max_age = if @ref && @commit && @ref == @commit.id # rubocop:disable Gitlab/ModuleWithInstanceVariables @@ -47,14 +42,9 @@ module SendsBlob end # Because we are opinionated we set the cache headers ourselves. - if Feature.enabled?(:improve_blobs_cache_headers) - expires_in(max_age, - public: allow_caching, must_revalidate: true, stale_if_error: 5.minutes, - stale_while_revalidate: 1.minute, 's-maxage': 1.minute) - else - response.cache_control[:public] = allow_caching - response.cache_control[:max_age] = max_age - end + expires_in(max_age, + public: allow_caching, must_revalidate: true, stale_if_error: 5.minutes, + stale_while_revalidate: 1.minute, 's-maxage': 1.minute) !stale end diff --git a/app/controllers/concerns/verifies_with_email.rb b/app/controllers/concerns/verifies_with_email.rb index 82388090350..fb48c0d8ba5 100644 --- a/app/controllers/concerns/verifies_with_email.rb +++ b/app/controllers/concerns/verifies_with_email.rb @@ -163,6 +163,7 @@ module VerifiesWithEmail end def require_email_verification_enabled?(user) - Feature.enabled?(:require_email_verification, user) + Feature.enabled?(:require_email_verification, user) && + Feature.disabled?(:skip_require_email_verification, user, type: :ops) end end diff --git a/app/controllers/concerns/zuora_csp.rb b/app/controllers/concerns/zuora_csp.rb deleted file mode 100644 index 5f9be11d7b9..00000000000 --- a/app/controllers/concerns/zuora_csp.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module ZuoraCSP - extend ActiveSupport::Concern - - ZUORA_URL = 'https://*.zuora.com' - - included do - content_security_policy do |policy| - next if policy.directives.blank? - - default_script_src = policy.directives['script-src'] || policy.directives['default-src'] - script_src_values = Array.wrap(default_script_src) | ["'self'", "'unsafe-eval'", ZUORA_URL] - - default_frame_src = policy.directives['frame-src'] || policy.directives['default-src'] - frame_src_values = Array.wrap(default_frame_src) | ["'self'", ZUORA_URL] - - default_child_src = policy.directives['child-src'] || policy.directives['default-src'] - child_src_values = Array.wrap(default_child_src) | ["'self'", ZUORA_URL] - - policy.script_src(*script_src_values) - policy.frame_src(*frame_src_values) - policy.child_src(*child_src_values) - end - end -end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 3d9184979d7..b003ca564f3 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -14,7 +14,7 @@ class DashboardController < Dashboard::ApplicationController respond_to :html - feature_category :users, [:activity] + feature_category :user_profile, [:activity] feature_category :team_planning, [:issues, :issues_calendar] feature_category :code_review_workflow, [:merge_requests] @@ -36,17 +36,20 @@ class DashboardController < Dashboard::ApplicationController def load_events @events = - if params[:filter] == "followed" - load_user_events - else + case params[:filter] + when "projects", "starred" load_project_events + when "followed" + load_user_events(current_user.followees) + else + load_user_events(current_user) end Events::RenderService.new(current_user).execute(@events) end - def load_user_events - UserRecentEventsFinder.new(current_user, current_user.followees, event_filter, params).execute + def load_user_events(user) + UserRecentEventsFinder.new(current_user, user, event_filter, params).execute end def load_project_events diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index 942cb9beed4..2f01bdecd23 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -144,7 +144,8 @@ class GraphqlController < ApplicationController def set_user_last_activity return unless current_user - Users::ActivityService.new(current_user).execute + # TODO: add namespace & project - https://gitlab.com/gitlab-org/gitlab/-/issues/387951 + Users::ActivityService.new(author: current_user).execute end def track_vs_code_usage diff --git a/app/controllers/groups/autocomplete_sources_controller.rb b/app/controllers/groups/autocomplete_sources_controller.rb index 6936733c4f7..3cad9e1fbad 100644 --- a/app/controllers/groups/autocomplete_sources_controller.rb +++ b/app/controllers/groups/autocomplete_sources_controller.rb @@ -46,6 +46,8 @@ class Groups::AutocompleteSourcesController < Groups::ApplicationController # rubocop: disable CodeReuse/ActiveRecord def target + # TODO https://gitlab.com/gitlab-org/gitlab/-/issues/388541 + # type_id is a misnomer. QuickActions::TargetService actually requires an iid. QuickActions::TargetService .new(nil, current_user, group: @group) .execute(params[:type], params[:type_id]) diff --git a/app/controllers/groups/email_campaigns_controller.rb b/app/controllers/groups/email_campaigns_controller.rb index 38087e3fc11..8ae429de490 100644 --- a/app/controllers/groups/email_campaigns_controller.rb +++ b/app/controllers/groups/email_campaigns_controller.rb @@ -3,7 +3,7 @@ class Groups::EmailCampaignsController < Groups::ApplicationController EMAIL_CAMPAIGNS_SCHEMA_URL = 'iglu:com.gitlab/email_campaigns/jsonschema/1-0-0' - feature_category :navigation + feature_category :experimentation_activation urgency :low before_action :check_params @@ -44,7 +44,7 @@ class Groups::EmailCampaignsController < Groups::ApplicationController when :team, :team_short group_group_members_url(group) when :admin_verify - project_settings_ci_cd_path(group.projects.first, ci_runner_templates: true, anchor: 'js-runners-settings') + project_settings_ci_cd_path(group.projects.first, anchor: 'js-runners-settings') end end diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb index 18b055b3f05..859bb0adb4e 100644 --- a/app/controllers/groups/runners_controller.rb +++ b/app/controllers/groups/runners_controller.rb @@ -9,8 +9,6 @@ class Groups::RunnersController < Groups::ApplicationController urgency :low def index - finder = Ci::RunnersFinder.new(current_user: current_user, params: { group: @group }) - @group_runners_limited_count = finder.execute.except(:limit, :offset).page.total_count_with_limit(:all, limit: 1000) @group_runner_registration_token = @group.runners_token if can?(current_user, :register_group_runners, group) Gitlab::Tracking.event(self.class.name, 'index', user: current_user, namespace: @group) diff --git a/app/controllers/groups/usage_quotas_controller.rb b/app/controllers/groups/usage_quotas_controller.rb index b660eb3af99..4f858cd130a 100644 --- a/app/controllers/groups/usage_quotas_controller.rb +++ b/app/controllers/groups/usage_quotas_controller.rb @@ -4,6 +4,7 @@ module Groups class UsageQuotasController < Groups::ApplicationController before_action :authorize_read_usage_quotas! before_action :verify_usage_quotas_enabled! + before_action :push_frontend_feature_flags feature_category :subscription_cost_management urgency :low @@ -15,6 +16,10 @@ module Groups private + def push_frontend_feature_flags + push_frontend_feature_flag(:usage_quotas_for_all_editions, @group) + end + def verify_usage_quotas_enabled! render_404 unless group.usage_quotas_enabled? end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index e440b60ad1f..8f7a2c177b7 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -36,9 +36,14 @@ class GroupsController < Groups::ApplicationController before_action only: :issues do push_frontend_feature_flag(:or_issuable_queries, group) + push_frontend_feature_flag(:frontend_caching, group) push_force_frontend_feature_flag(:work_items, group.work_items_feature_flag_enabled?) end + before_action only: :show do + push_frontend_feature_flag(:show_group_readme, group) + end + helper_method :captcha_required? skip_cross_project_access_check :index, :new, :create, :edit, :update, diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb index bedeae3cf54..d0e14000d8e 100644 --- a/app/controllers/ide_controller.rb +++ b/app/controllers/ide_controller.rb @@ -2,7 +2,6 @@ class IdeController < ApplicationController include VSCodeCDNCSP - include ClientsidePreviewCSP include StaticObjectExternalStorageCSP include Gitlab::Utils::StrongMemoize diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb index e9705c45116..f4eea3abd32 100644 --- a/app/controllers/import/bulk_imports_controller.rb +++ b/app/controllers/import/bulk_imports_controller.rb @@ -53,6 +53,7 @@ class Import::BulkImportsController < ApplicationController end def create + return render json: { success: false }, status: :too_many_requests if throttled_request? return render json: { success: false }, status: :unprocessable_entity unless valid_create_params? responses = create_params.map do |entry| @@ -204,4 +205,8 @@ class Import::BulkImportsController < ApplicationController def current_user_bulk_imports current_user.bulk_imports.gitlab end + + def throttled_request? + ::Gitlab::ApplicationRateLimiter.throttled_request?(request, current_user, :bulk_import, scope: current_user) + end end diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index 9a8f6a74653..0bee1faccf5 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -79,13 +79,7 @@ class Import::GithubController < Import::BaseController def realtime_changes Gitlab::PollingInterval.set_header(response, interval: 3_000) - render json: already_added_projects.map { |project| - { - id: project.id, - import_status: project.import_status, - stats: ::Gitlab::GithubImport::ObjectCounter.summary(project) - } - } + render json: Import::GithubRealtimeRepoSerializer.new.represent(already_added_projects) end def cancel @@ -99,6 +93,23 @@ class Import::GithubController < Import::BaseController end end + def cancel_all + projects_to_cancel = Project.imported_from(provider_name).created_by(current_user).is_importing + + canceled = projects_to_cancel.map do |project| + # #reset is called to make sure project was not finished/canceled brefore calling service + result = Import::Github::CancelProjectImportService.new(project.reset, current_user).execute + + { + id: project.id, + status: result[:status], + error: result[:message] + }.compact + end + + render json: canceled + end + protected override :importable_repos diff --git a/app/controllers/jira_connect/public_keys_controller.rb b/app/controllers/jira_connect/public_keys_controller.rb index 09003f8478f..4505ab16926 100644 --- a/app/controllers/jira_connect/public_keys_controller.rb +++ b/app/controllers/jira_connect/public_keys_controller.rb @@ -10,9 +10,7 @@ module JiraConnect skip_before_action :authenticate_user! def show - if Feature.disabled?(:jira_connect_oauth_self_managed) || !Gitlab.config.jira_connect.enable_public_keys_storage - return render_404 - end + return render_404 unless public_key_storage_enabled? render plain: public_key.key end @@ -22,5 +20,11 @@ module JiraConnect def public_key JiraConnect::PublicKey.find(params[:id]) end + + def public_key_storage_enabled? + return true if Gitlab.config.jira_connect.enable_public_keys_storage + + Gitlab::CurrentSettings.jira_connect_public_key_storage_enabled? + end end end diff --git a/app/controllers/jira_connect/subscriptions_controller.rb b/app/controllers/jira_connect/subscriptions_controller.rb index ff7477a94d6..a206e7fbbd8 100644 --- a/app/controllers/jira_connect/subscriptions_controller.rb +++ b/app/controllers/jira_connect/subscriptions_controller.rb @@ -21,7 +21,6 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController before_action do push_frontend_feature_flag(:jira_connect_oauth, @user) - push_frontend_feature_flag(:jira_connect_oauth_self_managed, @user) end before_action :allow_rendering_in_iframe, only: :index diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 5bd3b74af1f..4046433f8ea 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -61,14 +61,6 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController handle_omniauth end - def authentiq - if params['sid'] - handle_service_ticket oauth['provider'], params['sid'] - end - - handle_omniauth - end - def auth0 if oauth['uid'].blank? fail_auth0_login diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb index 55a2904ce83..829a87b7d0a 100644 --- a/app/controllers/profiles/avatars_controller.rb +++ b/app/controllers/profiles/avatars_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Profiles::AvatarsController < Profiles::ApplicationController - feature_category :users + feature_category :user_profile def destroy @user = current_user diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb index 7e332d9a498..c88616b6d6c 100644 --- a/app/controllers/profiles/emails_controller.rb +++ b/app/controllers/profiles/emails_controller.rb @@ -7,7 +7,7 @@ class Profiles::EmailsController < Profiles::ApplicationController before_action -> { check_rate_limit!(:profile_resend_email_confirmation, scope: current_user, redirect_back: true) }, only: [:resend_confirmation_instructions] - feature_category :users + feature_category :user_profile urgency :low, [:index] def index diff --git a/app/controllers/profiles/groups_controller.rb b/app/controllers/profiles/groups_controller.rb index 5962b10c44b..fdd76a7f7be 100644 --- a/app/controllers/profiles/groups_controller.rb +++ b/app/controllers/profiles/groups_controller.rb @@ -3,7 +3,7 @@ class Profiles::GroupsController < Profiles::ApplicationController include RoutableActions - feature_category :users + feature_category :user_profile def update group = find_routable!(Group, params[:id], request.fullpath) diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb index 39e8f6c500d..31c758ac763 100644 --- a/app/controllers/profiles/keys_controller.rb +++ b/app/controllers/profiles/keys_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Profiles::KeysController < Profiles::ApplicationController - feature_category :users + feature_category :user_profile urgency :low, [:create, :index] def index @@ -34,6 +34,16 @@ class Profiles::KeysController < Profiles::ApplicationController end end + def revoke + @key = current_user.keys.find(params[:id]) + Keys::RevokeService.new(current_user).execute(@key) + + respond_to do |format| + format.html { redirect_to profile_keys_url, status: :found } + format.js { head :ok } + end + end + private def key_params diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb index 974e7104c07..7786bad4251 100644 --- a/app/controllers/profiles/preferences_controller.rb +++ b/app/controllers/profiles/preferences_controller.rb @@ -3,7 +3,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController before_action :user - feature_category :users + feature_category :user_profile urgency :low, [:show] urgency :medium, [:update] diff --git a/app/controllers/profiles/saved_replies_controller.rb b/app/controllers/profiles/saved_replies_controller.rb new file mode 100644 index 00000000000..5ac5d645efb --- /dev/null +++ b/app/controllers/profiles/saved_replies_controller.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Profiles + class SavedRepliesController < Profiles::ApplicationController + feature_category :user_profile + + before_action do + render_404 unless Feature.enabled?(:saved_replies, current_user) + + @hide_search_settings = true + end + end +end diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index c36f03d3e69..aded295bfab 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -3,7 +3,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController skip_before_action :check_two_factor_requirement before_action :ensure_verified_primary_email, only: [:show, :create] - before_action :validate_current_password, only: [:create, :codes, :destroy], if: :current_password_required? + before_action :validate_current_password, only: [:create, :codes, :destroy, :create_webauthn], if: :current_password_required? before_action :update_current_user_otp!, only: [:show] helper_method :current_password_required? @@ -21,8 +21,13 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController def create otp_validation_result = ::Users::ValidateManualOtpService.new(current_user).execute(params[:pin_code]) + validated = (otp_validation_result[:status] == :success) - if otp_validation_result[:status] == :success + if validated && current_user.otp_backup_codes? && Feature.enabled?(:webauthn_without_totp) + ActiveSession.destroy_all_but_current(current_user, session) + Users::UpdateService.new(current_user, user: current_user, otp_required_for_login: true).execute! + redirect_to profile_two_factor_auth_path, notice: _("Your Time-based OTP device was registered!") + elsif validated ActiveSession.destroy_all_but_current(current_user, session) Users::UpdateService.new(current_user, user: current_user, otp_required_for_login: true).execute! do |user| @@ -64,10 +69,27 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController def create_webauthn @webauthn_registration = Webauthn::RegisterService.new(current_user, device_registration_params, session[:challenge]).execute + + notice = _("Your WebAuthn device was registered!") if @webauthn_registration.persisted? session.delete(:challenge) - redirect_to profile_two_factor_auth_path, notice: s_("Your WebAuthn device was registered!") + if Feature.enabled?(:webauthn_without_totp) + + if current_user.otp_backup_codes? + redirect_to profile_two_factor_auth_path, notice: notice + else + + Users::UpdateService.new(current_user, user: current_user).execute! do |user| + @codes = current_user.generate_otp_backup_codes! + end + helpers.dismiss_two_factor_auth_recovery_settings_check + flash[:notice] = notice + render 'create' + end + else + redirect_to profile_two_factor_auth_path, notice: notice + end else @qr_code = build_qr_code @@ -119,11 +141,17 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController end def validate_current_password + return if Feature.disabled?(:webauthn_without_totp) && params[:action] == 'create_webauthn' return if current_user.valid_password?(params[:current_password]) current_user.increment_failed_attempts! - @error = { message: _('You must provide a valid current password') } + error_message = { message: _('You must provide a valid current password.') } + if params[:action] == 'create_webauthn' + @webauthn_error = error_message + else + @error = error_message + end setup_show_page diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index e3704b77adc..45b274fc920 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -14,7 +14,7 @@ class ProfilesController < Profiles::ApplicationController push_frontend_feature_flag(:webauthn) end - feature_category :users, [:show, :update, :reset_incoming_email_token, :reset_feed_token, + feature_category :user_profile, [:show, :update, :reset_incoming_email_token, :reset_feed_token, :reset_static_object_token, :update_username] feature_category :authentication_and_authorization, [:audit_log] @@ -127,6 +127,7 @@ class ProfilesController < Profiles::ApplicationController :commit_email, :skype, :twitter, + :discord, :username, :website_url, :organization, diff --git a/app/controllers/projects/airflow/dags_controller.rb b/app/controllers/projects/airflow/dags_controller.rb new file mode 100644 index 00000000000..9d1f0b0d63b --- /dev/null +++ b/app/controllers/projects/airflow/dags_controller.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Projects + module Airflow + class DagsController < ::Projects::ApplicationController + before_action :check_feature_flag + before_action :authorize_read_airflow_dags! + + feature_category :dataops + + MAX_DAGS_PER_PAGE = 15 + def index + page = params[:page].to_i + page = 1 if page <= 0 + + @dags = ::Airflow::Dags.by_project_id(@project.id) + + return unless @dags.any? + + @dags = @dags.page(page).per(MAX_DAGS_PER_PAGE) + return redirect_to(url_for(page: @dags.total_pages)) if @dags.out_of_range? + + @pagination = { + page: page, + is_last_page: @dags.last_page?, + per_page: MAX_DAGS_PER_PAGE, + total_items: @dags.total_count + } + end + + private + + def check_feature_flag + render_404 unless Feature.enabled?(:airflow_dags, @project) + end + end + end +end diff --git a/app/controllers/projects/analytics/cycle_analytics/stages_controller.rb b/app/controllers/projects/analytics/cycle_analytics/stages_controller.rb index ab2cf3abdde..a61b774f9c8 100644 --- a/app/controllers/projects/analytics/cycle_analytics/stages_controller.rb +++ b/app/controllers/projects/analytics/cycle_analytics/stages_controller.rb @@ -3,7 +3,6 @@ class Projects::Analytics::CycleAnalytics::StagesController < Projects::ApplicationController include ::Analytics::CycleAnalytics::StageActions include Gitlab::Utils::StrongMemoize - extend ::Gitlab::Utils::Override respond_to :json @@ -11,20 +10,14 @@ class Projects::Analytics::CycleAnalytics::StagesController < Projects::Applicat before_action :authorize_read_cycle_analytics! before_action :only_default_value_stream_is_allowed! - before_action :authorize_stage!, only: [:median, :count, :average, :records] urgency :low private - override :parent - def parent - @project - end - - override :value_stream_class - def value_stream_class - Analytics::CycleAnalytics::ProjectValueStream + override :namespace + def namespace + @project.project_namespace end override :cycle_analytics_configuration @@ -33,7 +26,9 @@ class Projects::Analytics::CycleAnalytics::StagesController < Projects::Applicat end def only_default_value_stream_is_allowed! - render_404 if params[:value_stream_id] != Analytics::CycleAnalytics::Stages::BaseService::DEFAULT_VALUE_STREAM_NAME + return if requests_default_value_stream? + + render_403 end def permitted_stage?(stage) @@ -42,11 +37,20 @@ class Projects::Analytics::CycleAnalytics::StagesController < Projects::Applicat def permissions strong_memoize(:permissions) do - Gitlab::CycleAnalytics::Permissions.new(user: current_user, project: parent).get + Gitlab::CycleAnalytics::Permissions.new(user: current_user, project: @project).get end end - def authorize_stage! + def authorize_stage render_403 unless permitted_stage?(stage) end + + def requests_default_value_stream? + default_name = Analytics::CycleAnalytics::Stages::BaseService::DEFAULT_VALUE_STREAM_NAME + + params[:value_stream_id] == default_name + end end + +mod = 'Projects::Analytics::CycleAnalytics::StagesController' +Projects::Analytics::CycleAnalytics::StagesController.prepend_mod_with(mod) # rubocop: disable Cop/InjectEnterpriseEditionModule diff --git a/app/controllers/projects/analytics/cycle_analytics/value_streams_controller.rb b/app/controllers/projects/analytics/cycle_analytics/value_streams_controller.rb index 60bcd1d7238..f58730f1d33 100644 --- a/app/controllers/projects/analytics/cycle_analytics/value_streams_controller.rb +++ b/app/controllers/projects/analytics/cycle_analytics/value_streams_controller.rb @@ -1,17 +1,16 @@ # frozen_string_literal: true class Projects::Analytics::CycleAnalytics::ValueStreamsController < Projects::ApplicationController + include ::Analytics::CycleAnalytics::ValueStreamActions + respond_to :json feature_category :planning_analytics urgency :low - before_action :authorize_read_cycle_analytics! - - def index - # FOSS users can only see the default value stream - value_streams = [Analytics::CycleAnalytics::ProjectValueStream.build_default_value_stream(@project)] + private - render json: Analytics::CycleAnalytics::ValueStreamSerializer.new.represent(value_streams) + def namespace + project.project_namespace end end diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 25b83aed78a..62233c8c3c9 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -32,21 +32,6 @@ class Projects::ApplicationController < ApplicationController ->(project) { !project.pending_delete? } end - def authorize_read_build_trace! - return if can?(current_user, :read_build_trace, build) - - if build.debug_mode? - access_denied!( - _('You must have developer or higher permissions in the associated project to view job logs when debug trace ' \ - "is enabled. To disable debug trace, set the 'CI_DEBUG_TRACE' and 'CI_DEBUG_SERVICES' variables to 'false' " \ - 'in your pipeline configuration or CI/CD settings. If you must view this job log, a project maintainer ' \ - 'or owner must add you to the project with developer permissions or higher.') - ) - else - access_denied!(_('The current user is not authorized to access the job log.')) - end - end - def build_canonical_path(project) params[:namespace_id] = project.namespace.to_param params[:project_id] = project.to_param diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 3201538a393..5f8060ad756 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Projects::ArtifactsController < Projects::ApplicationController + include Ci::AuthBuildTrace include ExtractsPath include RendersBlob include SendFileUpload @@ -11,6 +12,7 @@ class Projects::ArtifactsController < Projects::ApplicationController layout 'project' before_action :authorize_read_build! before_action :authorize_read_build_trace!, only: [:download] + before_action :authorize_read_job_artifacts!, only: [:download] before_action :authorize_update_build!, only: [:keep] before_action :authorize_destroy_artifacts!, only: [:destroy] before_action :extract_ref_name_and_path @@ -40,10 +42,10 @@ class Projects::ArtifactsController < Projects::ApplicationController end def download - return render_404 unless artifacts_file + return render_404 unless artifact_file - log_artifacts_filesize(artifacts_file.model) - send_upload(artifacts_file, attachment: artifacts_file.filename, proxy: params[:proxy]) + log_artifacts_filesize(artifact_file.model) + send_upload(artifact_file, attachment: artifact_file.filename, proxy: params[:proxy]) end def browse @@ -82,11 +84,11 @@ class Projects::ArtifactsController < Projects::ApplicationController def raw return render_404 unless zip_artifact? - return render_404 unless artifacts_file + return render_404 unless artifact_file path = Gitlab::Ci::Build::Artifacts::Path.new(params[:path]) - send_artifacts_entry(artifacts_file, path) + send_artifacts_entry(artifact_file, path) end def keep @@ -153,8 +155,12 @@ class Projects::ArtifactsController < Projects::ApplicationController project.latest_successful_build_for_ref(params[:job], @ref_name) end - def artifacts_file - @artifacts_file ||= build&.artifacts_file_for_type(params[:file_type] || :archive) + def job_artifact + @job_artifact ||= build&.artifact_for_type(params[:file_type] || :archive) + end + + def artifact_file + @artifact_file ||= job_artifact&.file end def zip_artifact? @@ -175,4 +181,8 @@ class Projects::ArtifactsController < Projects::ApplicationController super end + + def authorize_read_job_artifacts! + return access_denied! unless can?(current_user, :read_job_artifacts, job_artifact) + end end diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb index 000203079cc..ffe6071ab3c 100644 --- a/app/controllers/projects/autocomplete_sources_controller.rb +++ b/app/controllers/projects/autocomplete_sources_controller.rb @@ -6,7 +6,7 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController feature_category :team_planning, [:issues, :labels, :milestones, :commands, :contacts] feature_category :code_review_workflow, [:merge_requests] - feature_category :users, [:members] + feature_category :user_profile, [:members] feature_category :source_code_management, [:snippets] urgency :low, [:merge_requests, :members] @@ -54,6 +54,8 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController # type_id is not required in general target_type = params.require(:type) + # TODO https://gitlab.com/gitlab-org/gitlab/-/issues/388541 + # type_id is a misnomer. QuickActions::TargetService actually requires an iid. QuickActions::TargetService .new(project, current_user) .execute(target_type, params[:type_id]) diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 4eda76f4f21..59cea00e26b 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -239,6 +239,8 @@ class Projects::BlobController < Projects::ApplicationController @last_commit = @repository.last_commit_for_path(@commit.id, @blob.path, literal_pathspec: true) @code_navigation_path = Gitlab::CodeNavigationPath.new(@project, @blob.commit_id).full_json_path_for(@blob.path) + allow_lfs_direct_download + render 'show' end @@ -282,6 +284,30 @@ class Projects::BlobController < Projects::ApplicationController def visitor_id current_user&.id end + + def allow_lfs_direct_download + return unless directly_downloading_lfs_object? && content_security_policy_enabled? + return unless (lfs_object = @project.lfs_objects.find_by_oid(@blob.lfs_oid)) + + request.content_security_policy.directives['connect-src'] ||= [] + request.content_security_policy.directives['connect-src'] << lfs_src(lfs_object) + end + + def directly_downloading_lfs_object? + Gitlab.config.lfs.enabled && + !Gitlab.config.lfs.object_store.proxy_download && + @blob&.stored_externally? + end + + def content_security_policy_enabled? + Gitlab.config.gitlab.content_security_policy.enabled + end + + def lfs_src(lfs_object) + file = lfs_object.file + file = file.cdn_enabled_url(request.remote_ip) if file.respond_to?(:cdn_enabled_url) + file.url + end end Projects::BlobController.prepend_mod diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index 7b01e4db42a..f19f143816f 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -19,8 +19,10 @@ class Projects::BranchesController < Projects::ApplicationController def index respond_to do |format| format.html do - @mode = params[:state].presence || 'overview' - @sort = sort_value_for_mode + @mode = fetch_mode + next render_404 unless @mode + + @sort = sort_param || default_sort @overview_max_branches = 5 # Fetch branches for the specified mode @@ -128,11 +130,7 @@ class Projects::BranchesController < Projects::ApplicationController private - def sort_value_for_mode - custom_sort || default_sort - end - - def custom_sort + def sort_param sort = params[:sort].presence unless sort.in?(supported_sort_options) @@ -144,11 +142,11 @@ class Projects::BranchesController < Projects::ApplicationController end def default_sort - 'stale' == @mode ? sort_value_oldest_updated : sort_value_recently_updated + 'stale' == @mode ? SORT_UPDATED_OLDEST : SORT_UPDATED_RECENT end def supported_sort_options - [nil, sort_value_name, sort_value_oldest_updated, sort_value_recently_updated] + [nil, SORT_NAME, SORT_UPDATED_OLDEST, SORT_UPDATED_RECENT] end # It can be expensive to calculate the diverging counts for each @@ -206,15 +204,23 @@ class Projects::BranchesController < Projects::ApplicationController limit = @overview_max_branches + 1 @active_branches = - BranchesFinder.new(@repository, { per_page: limit, sort: sort_value_recently_updated }) + BranchesFinder.new(@repository, { per_page: limit, sort: SORT_UPDATED_RECENT }) .execute(gitaly_pagination: true).select(&:active?) @stale_branches = - BranchesFinder.new(@repository, { per_page: limit, sort: sort_value_oldest_updated }) + BranchesFinder.new(@repository, { per_page: limit, sort: SORT_UPDATED_OLDEST }) .execute(gitaly_pagination: true).select(&:stale?) @branches = @active_branches + @stale_branches end + def fetch_mode + state = params[:state].presence + + return 'overview' unless state + + state.presence_in(%w[active stale all overview]) + end + def confidential_issue_project return if params[:confidential_issue_project_id].blank? diff --git a/app/controllers/projects/ci/pipeline_editor_controller.rb b/app/controllers/projects/ci/pipeline_editor_controller.rb index 1942a5fef7b..3a2bc445737 100644 --- a/app/controllers/projects/ci/pipeline_editor_controller.rb +++ b/app/controllers/projects/ci/pipeline_editor_controller.rb @@ -2,6 +2,9 @@ class Projects::Ci::PipelineEditorController < Projects::ApplicationController before_action :check_can_collaborate! + before_action do + push_frontend_feature_flag(:ci_job_assistant_drawer, @project) + end feature_category :pipeline_authoring diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 583b572d4b1..252b203b38a 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -31,6 +31,7 @@ class Projects::CommitController < Projects::ApplicationController respond_to do |format| format.html do + @ref = params[:id] render locals: { pagination_params: params.permit(:page) } end format.diff do diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index c006d56ae81..3acc71d5dd3 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -75,7 +75,7 @@ class Projects::CommitsController < Projects::ApplicationController search = permitted_params[:search] author = permitted_params[:author] - # fully_qualified_ref is available in some situations when the use_ref_type_parameter FF is enabled + # fully_qualified_ref is available in some situations from ExtractsRef ref = @fully_qualified_ref || @ref @commits = if search.present? diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index 63c1378ad11..9fe44659250 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -48,7 +48,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController end def load_value_stream - @value_stream = Analytics::CycleAnalytics::ProjectValueStream.build_default_value_stream(@project) + @value_stream = Analytics::CycleAnalytics::ValueStream.build_default_value_stream(@project.project_namespace) end def cycle_analytics_json diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index ea1288c0b20..9a88a8160b6 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -19,6 +19,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController before_action only: [:show] do push_frontend_feature_flag(:environment_details_vue, @project) end + before_action :authorize_read_environment!, except: [:metrics, :additional_metrics, :metrics_dashboard, :metrics_redirect] before_action :authorize_create_environment!, only: [:new, :create] before_action :authorize_stop_environment!, only: [:stop] @@ -57,6 +58,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController render json: { environments: serialize_environments(request, response, params[:nested]), review_app: serialize_review_app, + can_stop_stale_environments: can?(current_user, :stop_environment, @project), available_count: environments_count_by_state[:available], stopped_count: environments_count_by_state[:stopped] } diff --git a/app/controllers/projects/google_cloud/databases_controller.rb b/app/controllers/projects/google_cloud/databases_controller.rb index b511a85b0b8..9c20f10809c 100644 --- a/app/controllers/projects/google_cloud/databases_controller.rb +++ b/app/controllers/projects/google_cloud/databases_controller.rb @@ -51,7 +51,7 @@ module Projects if enable_response[:status] == :error track_event(:error_enable_cloudsql_services) - flash[:error] = error_message(enable_response[:message]) + flash[:alert] = error_message(enable_response[:message]) else create_response = ::GoogleCloud::CreateCloudsqlInstanceService .new(project, current_user, create_service_params) diff --git a/app/controllers/projects/google_cloud/deployments_controller.rb b/app/controllers/projects/google_cloud/deployments_controller.rb index fae8dbd59c7..92c99ad4271 100644 --- a/app/controllers/projects/google_cloud/deployments_controller.rb +++ b/app/controllers/projects/google_cloud/deployments_controller.rb @@ -22,7 +22,7 @@ class Projects::GoogleCloud::DeploymentsController < Projects::GoogleCloud::Base if enable_cloud_run_response[:status] == :error track_event(:error_enable_services) - flash[:error] = enable_cloud_run_response[:message] + flash[:alert] = enable_cloud_run_response[:message] redirect_to project_google_cloud_deployments_path(project) else params = { action: GoogleCloud::GeneratePipelineService::ACTION_DEPLOY_TO_CLOUD_RUN } @@ -31,7 +31,7 @@ class Projects::GoogleCloud::DeploymentsController < Projects::GoogleCloud::Base if generate_pipeline_response[:status] == :error track_event(:error_generate_cloudrun_pipeline) - flash[:error] = 'Failed to generate pipeline' + flash[:alert] = 'Failed to generate pipeline' redirect_to project_google_cloud_deployments_path(project) else cloud_run_mr_params = cloud_run_mr_params(generate_pipeline_response[:branch_name]) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 06c16297ce8..21227d62023 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -28,8 +28,7 @@ class Projects::IssuesController < Projects::ApplicationController SET_ISSUABLES_INDEX_ONLY_ACTIONS.include?(c.action_name.to_sym) && !index_html_request? } before_action :check_search_rate_limit!, if: ->(c) { - SET_ISSUABLES_INDEX_ONLY_ACTIONS.include?(c.action_name.to_sym) && !index_html_request? && - params[:search].present? && Feature.enabled?(:rate_limit_issuable_searches) + SET_ISSUABLES_INDEX_ONLY_ACTIONS.include?(c.action_name.to_sym) && !index_html_request? && params[:search].present? } # Allow write(create) issue @@ -47,6 +46,7 @@ class Projects::IssuesController < Projects::ApplicationController before_action do push_frontend_feature_flag(:preserve_unchanged_markdown, project) push_frontend_feature_flag(:content_editor_on_issues, project) + push_frontend_feature_flag(:service_desk_new_note_email_native_attachments, project) end before_action only: [:index, :show] do @@ -55,6 +55,7 @@ class Projects::IssuesController < Projects::ApplicationController before_action only: :index do push_frontend_feature_flag(:or_issuable_queries, project) + push_frontend_feature_flag(:frontend_caching, project&.group) end before_action only: :show do @@ -64,7 +65,7 @@ class Projects::IssuesController < Projects::ApplicationController push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?) push_frontend_feature_flag(:epic_widget_edit_confirmation, project) push_frontend_feature_flag(:use_iid_in_work_items_path, project&.group) - push_force_frontend_feature_flag(:work_items_create_from_markdown, project&.work_items_create_from_markdown_feature_flag_enabled?) + push_frontend_feature_flag(:incident_event_tags, project) end around_action :allow_gitaly_ref_name_caching, only: [:discussions] @@ -127,7 +128,7 @@ class Projects::IssuesController < Projects::ApplicationController discussion_to_resolve: params[:discussion_to_resolve], confidential: !!Gitlab::Utils.to_boolean(issue_params[:confidential]) ) - service = ::Issues::BuildService.new(project: project, current_user: current_user, params: build_params) + service = ::Issues::BuildService.new(container: project, current_user: current_user, params: build_params) @issue = @noteable = service.execute @@ -155,7 +156,7 @@ class Projects::IssuesController < Projects::ApplicationController ) spam_params = ::Spam::SpamParams.new_from_request(request: request) - service = ::Issues::CreateService.new(project: project, current_user: current_user, params: create_params, spam_params: spam_params) + service = ::Issues::CreateService.new(container: project, current_user: current_user, params: create_params, spam_params: spam_params) result = service.execute # Only irrecoverable errors such as unauthorized user won't contain an issue in the response @@ -190,7 +191,7 @@ class Projects::IssuesController < Projects::ApplicationController new_project = Project.find(params[:move_to_project_id]) return render_404 unless issue.can_move?(current_user, new_project) - @issue = ::Issues::MoveService.new(project: project, current_user: current_user).execute(issue, new_project) + @issue = ::Issues::MoveService.new(container: project, current_user: current_user).execute(issue, new_project) end respond_to do |format| @@ -204,7 +205,7 @@ class Projects::IssuesController < Projects::ApplicationController end def reorder - service = ::Issues::ReorderService.new(project: project, current_user: current_user, params: reorder_params) + service = ::Issues::ReorderService.new(container: project, current_user: current_user, params: reorder_params) if service.execute(issue) head :ok @@ -215,7 +216,7 @@ class Projects::IssuesController < Projects::ApplicationController def related_branches @related_branches = ::Issues::RelatedBranchesService - .new(project: project, current_user: current_user) + .new(container: project, current_user: current_user) .execute(issue) .map { |branch| branch.merge(link: branch_link(branch)) } @@ -370,7 +371,7 @@ class Projects::IssuesController < Projects::ApplicationController def update_service spam_params = ::Spam::SpamParams.new_from_request(request: request) - ::Issues::UpdateService.new(project: project, current_user: current_user, params: issue_params, spam_params: spam_params) + ::Issues::UpdateService.new(container: project, current_user: current_user, params: issue_params, spam_params: spam_params) end def finder_type diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index c6d442a6f27..3fea5c694f7 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -1,14 +1,15 @@ # frozen_string_literal: true class Projects::JobsController < Projects::ApplicationController + include Ci::AuthBuildTrace include SendFileUpload include ContinueParams include ProjectStatsRefreshConflictsGuard urgency :low, [:index, :show, :trace, :retry, :play, :cancel, :unschedule, :erase, :raw] - before_action :find_job_as_build, except: [:index, :play, :show] - before_action :find_job_as_processable, only: [:play, :show] + before_action :find_job_as_build, except: [:index, :play, :show, :retry] + before_action :find_job_as_processable, only: [:play, :show, :retry] before_action :authorize_read_build_trace!, only: [:trace, :raw] before_action :authorize_read_build! before_action :authorize_update_build!, @@ -76,7 +77,11 @@ class Projects::JobsController < Projects::ApplicationController response = Ci::RetryJobService.new(project, current_user).execute(@build) if response.success? - redirect_to build_path(response[:job]) + if @build.is_a?(::Ci::Build) + redirect_to build_path(response[:job]) + else + head :ok + end else respond_422 end diff --git a/app/controllers/projects/learn_gitlab_controller.rb b/app/controllers/projects/learn_gitlab_controller.rb deleted file mode 100644 index 6fe009c8a28..00000000000 --- a/app/controllers/projects/learn_gitlab_controller.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -class Projects::LearnGitlabController < Projects::ApplicationController - before_action :authenticate_user! - before_action :check_experiment_enabled? - before_action :enable_invite_for_help_continuous_onboarding_experiment - before_action :enable_video_tutorials_continuous_onboarding_experiment - - feature_category :users - urgency :low, [:index] - - def index - end - - private - - def check_experiment_enabled? - return access_denied! unless helpers.learn_gitlab_enabled?(project) - end - - def enable_invite_for_help_continuous_onboarding_experiment - return unless current_user.can?(:admin_group_member, project.namespace) - - experiment(:invite_for_help_continuous_onboarding, namespace: project.namespace) do |e| - e.candidate {} - end - end - - def enable_video_tutorials_continuous_onboarding_experiment - experiment(:video_tutorials_continuous_onboarding, namespace: project&.namespace).publish - end -end diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index cba0056ccd5..3b399e3294e 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -20,10 +20,6 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap :branch_to ] - before_action do - push_frontend_feature_flag(:mr_compare_dropdowns, project) - end - def new define_new_vars end @@ -97,7 +93,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap def target_projects projects = MergeRequestTargetProjectFinder .new(current_user: current_user, source_project: @project, project_feature: :repository) - .execute(include_routes: true).limit(20).search(params[:search]) + .execute(include_routes: false, search: params[:search]).limit(20) render json: ProjectSerializer.new.represent(projects) end diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index 1c546d70df9..6ca885cee4c 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -61,6 +61,8 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic options[:merge_ref_head_diff] ] + expires_in(1.day) if cache_with_max_age? + return unless stale?(etag: [cache_context + diff_options_hash.fetch(:paths, []), diffs]) Gitlab::Metrics.measure(:diffs_unfold) do @@ -238,4 +240,10 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter .track_mr_diffs_single_file_action(merge_request: @merge_request, user: current_user) end + + def cache_with_max_age? + @merge_request.diffs_batch_cache_with_max_age? && + params[:ck].present? && + render_merge_ref_head_diff? + end end diff --git a/app/controllers/projects/merge_requests/drafts_controller.rb b/app/controllers/projects/merge_requests/drafts_controller.rb index 74bb3ad1a63..ca6ab83b877 100644 --- a/app/controllers/projects/merge_requests/drafts_controller.rb +++ b/app/controllers/projects/merge_requests/drafts_controller.rb @@ -49,24 +49,22 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli def publish result = DraftNotes::PublishService.new(merge_request, current_user).execute(draft_note(allow_nil: true)) - if Feature.enabled?(:mr_review_submit_comment, @project) - if create_note_params[:note] - ::Notes::CreateService.new(@project, current_user, create_note_params).execute + if create_note_params[:note] + ::Notes::CreateService.new(@project, current_user, create_note_params).execute - merge_request_activity_counter.track_submit_review_comment(user: current_user) - end + merge_request_activity_counter.track_submit_review_comment(user: current_user) + end - if Gitlab::Utils.to_boolean(approve_params[:approve]) - unless merge_request.approved_by?(current_user) - success = ::MergeRequests::ApprovalService.new(project: @project, current_user: current_user, params: approve_params).execute(merge_request) + if Gitlab::Utils.to_boolean(approve_params[:approve]) + unless merge_request.approved_by?(current_user) + success = ::MergeRequests::ApprovalService.new(project: @project, current_user: current_user, params: approve_params).execute(merge_request) - unless success - return render json: { message: _('An error occurred while approving, please try again.') }, status: :internal_server_error - end + unless success + return render json: { message: _('An error occurred while approving, please try again.') }, status: :internal_server_error end - - merge_request_activity_counter.track_submit_review_approve(user: current_user) end + + merge_request_activity_counter.track_submit_review_approve(user: current_user) end if result[:status] == :success @@ -145,7 +143,7 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli user_ids = notes.map(&:author_id) project.team.max_member_access_for_user_ids(user_ids) - notes.map(&method(:render_draft_note)) + notes.map { |note| render_draft_note(note) } end def render_draft_note(note) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index b0920b3fbdb..d92ef3de6d9 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -28,9 +28,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo :codequality_mr_diff_reports ] before_action :set_issuables_index, only: [:index] - before_action :check_search_rate_limit!, only: [:index], if: -> { - params[:search].present? && Feature.enabled?(:rate_limit_issuable_searches) - } + before_action :check_search_rate_limit!, only: [:index], if: -> { params[:search].present? } before_action :authenticate_user!, only: [:assign_related_issues] before_action :check_user_can_push_to_source_branch!, only: [:rebase] @@ -40,9 +38,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:refactor_security_extension, @project) push_frontend_feature_flag(:refactor_code_quality_inline_findings, project) push_frontend_feature_flag(:moved_mr_sidebar, project) - push_frontend_feature_flag(:mr_review_submit_comment, project) push_frontend_feature_flag(:mr_experience_survey, project) - push_frontend_feature_flag(:realtime_reviewers, project) push_frontend_feature_flag(:realtime_mr_status_change, project) end @@ -282,11 +278,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo case result[:count] when 0 - flash[:error] = "Failed to assign you issues related to the merge request" - when 1 - flash[:notice] = "1 issue has been assigned to you" + flash[:alert] = _("Failed to assign you issues related to the merge request.") else - flash[:notice] = "#{result[:count]} issues have been assigned to you" + flash[:notice] = n_("An issue has been assigned to you.", "%d issues have been assigned to you.", result[:count]) end redirect_to(merge_request_path(@merge_request)) @@ -356,10 +350,20 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo private + # NOTE: Remove this disable with add_prepared_state_to_mr FF removal + # rubocop: disable Metrics/AbcSize def show_merge_request close_merge_request_if_no_source_project @merge_request.check_mergeability(async: true) + # NOTE: Remove the created_at check when removing the FF check + if ::Feature.enabled?(:add_prepared_state_to_mr, @merge_request.project) && + @merge_request.created_at < 5.minutes.ago && + !@merge_request.prepared? + + @merge_request.prepare + end + respond_to do |format| format.html do # use next to appease Rubocop @@ -401,6 +405,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo end end end + # rubocop: enable Metrics/AbcSize def render_html_page preload_assignees_for_render(@merge_request) @@ -419,6 +424,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo @update_current_user_path = expose_path(api_v4_user_preferences_path) @endpoint_metadata_url = endpoint_metadata_url(@project, @merge_request) @endpoint_diff_batch_url = endpoint_diff_batch_url(@project, @merge_request) + @diffs_batch_cache_key = @merge_request.merge_head_diff&.id if merge_request.diffs_batch_cache_with_max_age? set_pipeline_variables @@ -576,6 +582,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo def endpoint_diff_batch_url(project, merge_request) per_page = current_user&.view_diffs_file_by_file ? '1' : '5' params = request.query_parameters.merge(view: 'inline', diff_head: true, w: show_whitespace, page: '0', per_page: per_page) + params[:ck] = merge_request.merge_head_diff&.id if merge_request.diffs_batch_cache_with_max_age? diffs_batch_project_json_merge_request_path(project, merge_request, 'json', params) end diff --git a/app/controllers/projects/ml/experiments_controller.rb b/app/controllers/projects/ml/experiments_controller.rb index 1e1c4b1587c..00b965542f6 100644 --- a/app/controllers/projects/ml/experiments_controller.rb +++ b/app/controllers/projects/ml/experiments_controller.rb @@ -3,6 +3,8 @@ module Projects module Ml class ExperimentsController < ::Projects::ApplicationController + include Projects::Ml::ExperimentsHelper + before_action :check_feature_flag feature_category :mlops @@ -11,7 +13,12 @@ module Projects MAX_CANDIDATES_PER_PAGE = 30 def index - @experiments = ::Ml::Experiment.by_project_id(@project.id).page(params[:page]).per(MAX_EXPERIMENTS_PER_PAGE) + paginator = ::Ml::Experiment.by_project_id(@project.id) + .with_candidate_count + .keyset_paginate(cursor: params[:cursor], per_page: MAX_EXPERIMENTS_PER_PAGE) + + @experiments = paginator.records + @page_info = page_info(paginator) end def show @@ -19,26 +26,17 @@ module Projects return redirect_to project_ml_experiments_path(@project) unless @experiment.present? - page = params[:page].to_i - page = 1 if page == 0 - - @candidates = @experiment.candidates - .including_relationships - .page(page) - .per(MAX_CANDIDATES_PER_PAGE) - - return unless @candidates - - return redirect_to(url_for(page: @candidates.total_pages)) if @candidates.out_of_range? + find_params = params + .transform_keys(&:underscore) + .permit(:name, :order_by, :sort, :order_by_type) - @pagination = { - page: page, - is_last_page: @candidates.last_page?, - per_page: MAX_CANDIDATES_PER_PAGE, - total_items: @candidates.total_count - } + paginator = CandidateFinder + .new(@experiment, find_params) + .execute + .keyset_paginate(cursor: params[:cursor], per_page: MAX_CANDIDATES_PER_PAGE) - @candidates.each(&:artifact_lazy) + @candidates = paginator.records.each(&:artifact_lazy) + @page_info = page_info(paginator) end private diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb index aa0838752e2..f3c63b1b97b 100644 --- a/app/controllers/projects/network_controller.rb +++ b/app/controllers/projects/network_controller.rb @@ -14,11 +14,7 @@ class Projects::NetworkController < Projects::ApplicationController urgency :low, [:show] def show - @url = if Feature.enabled?(:use_ref_type_parameter, @project) - project_network_path(@project, @ref, @options.merge(format: :json, ref_type: ref_type)) - else - project_network_path(@project, @ref, @options.merge(format: :json)) - end + @url = project_network_path(@project, @ref, @options.merge(format: :json, ref_type: ref_type)) @ref_type = ref_type @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 9d3506d49b0..054e8c302c9 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -12,7 +12,8 @@ class Projects::NotesController < Projects::ApplicationController before_action :authorize_resolve_note!, only: [:resolve, :unresolve] feature_category :team_planning - urgency :low + urgency :medium, [:index] + urgency :low, [:create, :update, :destroy, :resolve, :unresolve, :toggle_award_emoji, :outdated_line_change] def delete_attachment note.remove_attachment! diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb index 31030d958df..19d031bd59b 100644 --- a/app/controllers/projects/pipeline_schedules_controller.rb +++ b/app/controllers/projects/pipeline_schedules_controller.rb @@ -41,7 +41,9 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController end def update - if schedule.update(schedule_params) + response = Ci::PipelineSchedules::UpdateService.new(schedule, current_user, schedule_params).execute + + if response.success? redirect_to project_pipeline_schedules_path(@project) else render :edit @@ -63,7 +65,9 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController end def take_ownership - if schedule.update(owner: current_user) + response = Ci::PipelineSchedules::TakeOwnershipService.new(schedule, current_user).execute + + if response.success? redirect_to pipeline_schedules_path(@project) else redirect_to pipeline_schedules_path(@project), alert: _("Failed to change the owner") diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index db77127cb0a..10f58a9f479 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -5,7 +5,6 @@ class Projects::PipelinesController < Projects::ApplicationController include RedisTracking include ProductAnalyticsTracking include ProjectStatsRefreshConflictsGuard - include ZuoraCSP urgency :low, [ :index, :new, :builds, :show, :failures, :create, @@ -220,6 +219,8 @@ class Projects::PipelinesController < Projects::ApplicationController def config_variables respond_to do |format| format.json do + # Even if the parameter name is `sha`, it is actually a ref name. We always send `ref` to the endpoint. + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/389065 result = Ci::ListConfigVariablesService.new(@project, current_user).execute(params[:sha]) result.nil? ? head(:no_content) : render(json: result) diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index cd9c6efb106..543ffa637e1 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -47,7 +47,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController end def membershipable_members - project.members + query_members_via_project_namespace_enabled? ? project.namespace_members : project.members end def plain_source_type @@ -65,6 +65,18 @@ class Projects::ProjectMembersController < Projects::ApplicationController def root_params_key :project_member end + + def members_and_requesters + query_members_via_project_namespace_enabled? ? project.namespace_members_and_requesters : super + end + + def requesters + query_members_via_project_namespace_enabled? ? project.namespace_requesters : super + end + + def query_members_via_project_namespace_enabled? + Feature.enabled?(:project_members_index_by_project_namespace, project) + end end Projects::ProjectMembersController.prepend_mod_with('Projects::ProjectMembersController') diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index 924de0ee7ea..895a9a00624 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -10,7 +10,7 @@ class Projects::RawController < Projects::ApplicationController prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:blob) } - before_action :set_ref_and_path + before_action :assign_ref_vars before_action :require_non_empty_project before_action :authorize_read_code! before_action :check_show_rate_limit!, only: [:show], unless: :external_storage_request? diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index 8ac6d872aae..4c2bd2a9d42 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -24,17 +24,9 @@ class Projects::RefsController < Projects::ApplicationController when "blob" project_blob_path(@project, @id) when "graph" - if Feature.enabled?(:use_ref_type_parameter, @project) - project_network_path(@project, @id, ref_type: ref_type) - else - project_network_path(@project, @id, @options) - end + project_network_path(@project, @id, ref_type: ref_type) when "graphs" - if Feature.enabled?(:use_ref_type_parameter, @project) - project_graph_path(@project, @id, ref_type: ref_type) - else - project_graph_path(@project, @id) - end + project_graph_path(@project, @id, ref_type: ref_type) when "find_file" project_find_file_path(@project, @id) when "graphs_commits" @@ -42,11 +34,7 @@ class Projects::RefsController < Projects::ApplicationController when "badges" project_settings_ci_cd_path(@project, ref: @id) else - if Feature.enabled?(:use_ref_type_parameter, @project) - project_commits_path(@project, @id, ref_type: ref_type) - else - project_commits_path(@project, @id) - end + project_commits_path(@project, @id, ref_type: ref_type) end redirect_to new_path diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb index da414d068a6..7c569df7267 100644 --- a/app/controllers/projects/releases_controller.rb +++ b/app/controllers/projects/releases_controller.rb @@ -9,6 +9,10 @@ class Projects::ReleasesController < Projects::ApplicationController before_action :authorize_create_release!, only: :new before_action :validate_suffix_path, :fetch_latest_tag, only: :latest_permalink + prepend_before_action(only: [:downloads]) do + authenticate_sessionless_user!(:download) + end + feature_category :release_orchestration urgency :low diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb index 33ce37ef4fb..1cd4c5b6137 100644 --- a/app/controllers/projects/repositories_controller.rb +++ b/app/controllers/projects/repositories_controller.rb @@ -47,8 +47,13 @@ class Projects::RepositoriesController < Projects::ApplicationController end def set_cache_headers - expires_in cache_max_age(archive_metadata['CommitId']), public: Guest.can?(:download_code, project) - fresh_when(etag: archive_metadata['ArchivePath']) + commit_id = archive_metadata['CommitId'] + + expires_in(cache_max_age(commit_id), + public: Guest.can?(:download_code, project), must_revalidate: true, stale_if_error: 5.minutes, + stale_while_revalidate: 1.minute, 's-maxage': 1.minute) + + fresh_when(strong_etag: [commit_id, archive_metadata['ArchivePath']]) end def archive_not_modified? diff --git a/app/controllers/projects/service_ping_controller.rb b/app/controllers/projects/service_ping_controller.rb index cfc322b47e7..8c16b6b230e 100644 --- a/app/controllers/projects/service_ping_controller.rb +++ b/app/controllers/projects/service_ping_controller.rb @@ -5,24 +5,6 @@ class Projects::ServicePingController < Projects::ApplicationController feature_category :web_ide - def web_ide_clientside_preview - return render_404 unless Gitlab::CurrentSettings.web_ide_clientside_preview_enabled? - - Gitlab::UsageDataCounters::WebIdeCounter.increment_previews_count - - head(:ok) - end - - def web_ide_clientside_preview_success - return render_404 unless Gitlab::CurrentSettings.web_ide_clientside_preview_enabled? - - Gitlab::UsageDataCounters::WebIdeCounter.increment_previews_success_count - Gitlab::UsageDataCounters::EditorUniqueCounter.track_live_preview_edit_action(author: current_user, - project: project) - - head(:ok) - end - def web_ide_pipelines_count Gitlab::UsageDataCounters::WebIdeCounter.increment_pipelines_count diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index f8133c5836d..4ca665679c0 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -4,7 +4,6 @@ module Projects module Settings class CiCdController < Projects::ApplicationController include RunnerSetupScripts - include ZuoraCSP NUMBER_OF_RUNNERS_PER_PAGE = 20 @@ -13,6 +12,10 @@ module Projects before_action :check_builds_available! before_action :define_variables + before_action do + push_frontend_feature_flag(:ci_inbound_job_token_scope, @project) + end + helper_method :highlight_badge feature_category :continuous_integration diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb index 6d099aa8b3d..74d730db026 100644 --- a/app/controllers/projects/settings/repository_controller.rb +++ b/app/controllers/projects/settings/repository_controller.rb @@ -88,25 +88,20 @@ module Projects # rubocop: disable CodeReuse/ActiveRecord def define_protected_refs - @protected_branches = @project.protected_branches.order(:name).page(params[:page]) + @protected_branches = fetch_protected_branches(@project) @protected_tags = @project.protected_tags.order(:name).page(params[:page]) @protected_branch = @project.protected_branches.new @protected_tag = @project.protected_tags.new @protected_tags_count = @protected_tags.reduce(0) { |sum, tag| sum + tag.matching(@project.repository.tag_names).size } - - if Feature.enabled?(:group_protected_branches) - @protected_group_branches = if @project.root_namespace.is_a?(Group) - @project.root_namespace.protected_branches.order(:name).page(params[:page]) - else - [] - end - end - load_gon_index end # rubocop: enable CodeReuse/ActiveRecord + def fetch_protected_branches(project) + project.protected_branches.sorted_by_name.page(params[:page]) + end + def remote_mirror @remote_mirror = project.remote_mirrors.first_or_initialize end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index ee2c268ff33..71ad747b6b1 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -45,7 +45,6 @@ class ProjectsController < Projects::ApplicationController push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items_mvc, @project&.work_items_mvc_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?) - push_frontend_feature_flag(:package_registry_access_level) end layout :determine_layout @@ -223,7 +222,22 @@ class ProjectsController < Projects::ApplicationController end def housekeeping - ::Repositories::HousekeepingService.new(@project, :gc).execute + task = if params[:prune].present? + :prune + else + :eager + end + + ::Repositories::HousekeepingService.new(@project, task).execute do + ::Gitlab::Audit::Auditor.audit( + name: 'manually_trigger_housekeeping', + author: current_user, + scope: @project, + target: @project, + message: "Housekeeping task: #{task}", + created_at: DateTime.current + ) + end redirect_to( project_path(@project), diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 0800d635d92..ed0e019d02b 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -115,6 +115,7 @@ class RegistrationsController < Devise::RegistrationsController def after_request_hook(user) return unless user.persisted? + track_creation user: user Gitlab::Tracking.event(self.class.name, 'successfully_submitted_form', user: user) end @@ -145,6 +146,11 @@ class RegistrationsController < Devise::RegistrationsController users_sign_up_welcome_path(glm_tracking_params) end + def track_creation(user:) + label = user_invited? ? 'invited' : 'signup' + Gitlab::Tracking.event(self.class.name, 'create_user', label: label, user: user) + end + def ensure_destroy_prerequisites_met if current_user.solo_owned_groups.present? redirect_to profile_account_path, @@ -252,9 +258,15 @@ class RegistrationsController < Devise::RegistrationsController end end - def after_pending_invitations_hook - member_id = session.delete(:originating_member_id) + def user_invited? + !!member_id + end + def member_id + @member_id ||= session.delete(:originating_member_id) + end + + def after_pending_invitations_hook return unless member_id # if invited multiple times to different projects, only the email clicked will be counted as accepted diff --git a/app/controllers/repositories/git_http_controller.rb b/app/controllers/repositories/git_http_controller.rb index 144ec4c0de9..bd3461d8331 100644 --- a/app/controllers/repositories/git_http_controller.rb +++ b/app/controllers/repositories/git_http_controller.rb @@ -116,7 +116,7 @@ module Repositories end def log_user_activity - Users::ActivityService.new(user).execute + Users::ActivityService.new(author: user, project: project, namespace: project&.namespace).execute end end end diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index 97b6671a82a..71da9bdcbc4 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -41,8 +41,10 @@ class RootController < Dashboard::ProjectsController when 'stars' flash.keep redirect_to(starred_dashboard_projects_path) - when 'project_activity' + when 'your_activity' redirect_to(activity_dashboard_path) + when 'project_activity' + redirect_to(activity_dashboard_path(filter: 'projects')) when 'starred_project_activity' redirect_to(activity_dashboard_path(filter: 'starred')) when 'followed_user_activity' diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 38c773fa31d..1ca34dee3d6 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -24,6 +24,8 @@ class SearchController < ApplicationController before_action :block_anonymous_global_searches, :check_scope_global_search_enabled, except: :opensearch skip_before_action :authenticate_user! + skip_before_action :default_cache_headers, only: :count + requires_cross_project_access if: -> do search_term_present = params[:search].present? || params[:term].present? search_term_present && !params[:project_id].present? @@ -31,7 +33,7 @@ class SearchController < ApplicationController before_action :check_search_rate_limit!, only: search_rate_limited_endpoints before_action only: :show do - push_frontend_feature_flag(:search_page_vertical_nav, current_user) + push_frontend_feature_flag(:search_blobs_language_aggregation, current_user) end before_action only: :show do update_scope_for_code_search @@ -65,6 +67,8 @@ class SearchController < ApplicationController @search_highlight = @search_service_presenter.search_highlight end + return if @search_results.respond_to?(:failed?) && @search_results.failed? + Gitlab::Metrics::GlobalSearchSlis.record_apdex( elapsed: @global_search_duration_s, search_type: @search_type, diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 699dcf1adac..b6aba04c877 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -289,7 +289,7 @@ class SessionsController < Devise::SessionsController def log_user_activity(user) login_counter.increment - Users::ActivityService.new(user).execute + Users::ActivityService.new(author: user).execute end def load_recaptcha diff --git a/app/controllers/users/unsubscribes_controller.rb b/app/controllers/users/unsubscribes_controller.rb index 9ac07083cd5..73388b16006 100644 --- a/app/controllers/users/unsubscribes_controller.rb +++ b/app/controllers/users/unsubscribes_controller.rb @@ -4,7 +4,7 @@ module Users class UnsubscribesController < ApplicationController skip_before_action :authenticate_user! - feature_category :users + feature_category :user_profile def show @user = get_user diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index f23e513e419..9546f71cd37 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -26,8 +26,11 @@ class UsersController < ApplicationController before_action only: [:exists] do check_rate_limit!(:username_exists, scope: request.ip) end + before_action only: [:show] do + push_frontend_feature_flag(:profile_tabs_vue, current_user) + end - feature_category :users, [:show, :activity, :groups, :projects, :contributed, :starred, + feature_category :user_profile, [:show, :activity, :groups, :projects, :contributed, :starred, :followers, :following, :calendar, :calendar_activities, :exists, :activity, :follow, :unfollow, :ssh_keys] diff --git a/app/controllers/whats_new_controller.rb b/app/controllers/whats_new_controller.rb index 4decd7f1bee..03b9c49de96 100644 --- a/app/controllers/whats_new_controller.rb +++ b/app/controllers/whats_new_controller.rb @@ -8,7 +8,7 @@ class WhatsNewController < ApplicationController before_action :check_whats_new_enabled before_action :check_valid_page_param, :set_pagination_headers - feature_category :navigation + feature_category :onboarding urgency :low def index |