diff options
Diffstat (limited to 'app/controllers')
94 files changed, 672 insertions, 339 deletions
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 1d0930ba73c..0dd85376050 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -66,12 +66,17 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController render html: Gitlab::Highlight.highlight('payload.json', usage_data_json, language: 'json') end - format.json { render json: Gitlab::Usage::ServicePingReport.for(output: :all_metrics_values, cached: true).to_json } + + format.json do + Gitlab::UsageDataCounters::ServiceUsageDataCounter.count(:download_payload_click) + + render json: Gitlab::Usage::ServicePingReport.for(output: :all_metrics_values, cached: true).to_json + end end end def reset_registration_token - @application_setting.reset_runners_registration_token! + ::Ci::Runners::ResetRegistrationTokenService.new(@application_setting, current_user).execute flash[:notice] = _('New runners registration token has been generated!') redirect_to admin_runners_path diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb index 4660b0bfbb0..ef843a84e6c 100644 --- a/app/controllers/admin/broadcast_messages_controller.rb +++ b/app/controllers/admin/broadcast_messages_controller.rb @@ -65,6 +65,6 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController target_path broadcast_type dismissable - )) + ), target_access_levels: []).reverse_merge!(target_access_levels: []) end end diff --git a/app/controllers/admin/clusters_controller.rb b/app/controllers/admin/clusters_controller.rb index 9a642e53d86..052c8821588 100644 --- a/app/controllers/admin/clusters_controller.rb +++ b/app/controllers/admin/clusters_controller.rb @@ -2,6 +2,7 @@ class Admin::ClustersController < Clusters::ClustersController include EnforcesAdminAuthentication + before_action :ensure_feature_enabled! layout 'admin' diff --git a/app/controllers/admin/cohorts_controller.rb b/app/controllers/admin/cohorts_controller.rb index e750b5c5ad4..468a1077694 100644 --- a/app/controllers/admin/cohorts_controller.rb +++ b/app/controllers/admin/cohorts_controller.rb @@ -5,6 +5,8 @@ class Admin::CohortsController < Admin::ApplicationController feature_category :devops_reports + urgency :low + def index @cohorts = load_cohorts track_cohorts_visit diff --git a/app/controllers/admin/dev_ops_report_controller.rb b/app/controllers/admin/dev_ops_report_controller.rb index a235af7c538..47e3337aed7 100644 --- a/app/controllers/admin/dev_ops_report_controller.rb +++ b/app/controllers/admin/dev_ops_report_controller.rb @@ -9,6 +9,8 @@ class Admin::DevOpsReportController < Admin::ApplicationController feature_category :devops_reports + urgency :low + # rubocop: disable CodeReuse/ActiveRecord def show @metric = DevOpsReport::Metric.order(:created_at).last&.present diff --git a/app/controllers/admin/instance_review_controller.rb b/app/controllers/admin/instance_review_controller.rb index 1ce6e66c6de..cc801bce5b7 100644 --- a/app/controllers/admin/instance_review_controller.rb +++ b/app/controllers/admin/instance_review_controller.rb @@ -2,6 +2,8 @@ class Admin::InstanceReviewController < Admin::ApplicationController feature_category :devops_reports + urgency :low + def index redirect_to("#{Gitlab::SubscriptionPortal.subscriptions_instance_review_url}?#{instance_review_params}") end diff --git a/app/controllers/admin/integrations_controller.rb b/app/controllers/admin/integrations_controller.rb index ad0ee0b2cef..db9835e65ec 100644 --- a/app/controllers/admin/integrations_controller.rb +++ b/app/controllers/admin/integrations_controller.rb @@ -5,6 +5,10 @@ class Admin::IntegrationsController < Admin::ApplicationController before_action :not_found, unless: -> { instance_level_integrations? } + before_action do + push_frontend_feature_flag(:integration_form_sections, default_enabled: :yaml) + end + feature_category :integrations def overrides diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb index 598c536d652..a4055cbe990 100644 --- a/app/controllers/admin/runner_projects_controller.rb +++ b/app/controllers/admin/runner_projects_controller.rb @@ -8,7 +8,7 @@ class Admin::RunnerProjectsController < Admin::ApplicationController def create @runner = Ci::Runner.find(params[:runner_project][:runner_id]) - if @runner.assign_to(@project, current_user) + if ::Ci::Runners::AssignRunnerService.new(@runner, @project, current_user).execute redirect_to edit_admin_runner_url(@runner), notice: s_('Runners|Runner assigned to project.') else redirect_to edit_admin_runner_url(@runner), alert: 'Failed adding runner to project' @@ -18,7 +18,8 @@ class Admin::RunnerProjectsController < Admin::ApplicationController def destroy rp = Ci::RunnerProject.find(params[:id]) runner = rp.runner - rp.destroy + + ::Ci::Runners::UnassignRunnerService.new(rp, current_user).execute redirect_to edit_admin_runner_url(runner), status: :found, notice: s_('Runners|Runner unassigned from project.') end diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb index f7f78ab3229..2744be0150c 100644 --- a/app/controllers/admin/runners_controller.rb +++ b/app/controllers/admin/runners_controller.rb @@ -23,7 +23,7 @@ class Admin::RunnersController < Admin::ApplicationController end def update - if Ci::UpdateRunnerService.new(@runner).update(runner_params) + if Ci::Runners::UpdateRunnerService.new(@runner).update(runner_params) respond_to do |format| format.html { redirect_to edit_admin_runner_path(@runner) } end @@ -34,13 +34,13 @@ class Admin::RunnersController < Admin::ApplicationController end def destroy - Ci::UnregisterRunnerService.new(@runner).execute + Ci::Runners::UnregisterRunnerService.new(@runner, current_user).execute redirect_to admin_runners_path, status: :found end def resume - if Ci::UpdateRunnerService.new(@runner).update(active: true) + if Ci::Runners::UpdateRunnerService.new(@runner).update(active: true) redirect_to admin_runners_path, notice: _('Runner was successfully updated.') else redirect_to admin_runners_path, alert: _('Runner was not updated.') @@ -48,7 +48,7 @@ class Admin::RunnersController < Admin::ApplicationController end def pause - if Ci::UpdateRunnerService.new(@runner).update(active: false) + if Ci::Runners::UpdateRunnerService.new(@runner).update(active: false) redirect_to admin_runners_path, notice: _('Runner was successfully updated.') else redirect_to admin_runners_path, alert: _('Runner was not updated.') diff --git a/app/controllers/admin/usage_trends_controller.rb b/app/controllers/admin/usage_trends_controller.rb index 0b315517594..2cede1aec05 100644 --- a/app/controllers/admin/usage_trends_controller.rb +++ b/app/controllers/admin/usage_trends_controller.rb @@ -7,6 +7,8 @@ class Admin::UsageTrendsController < Admin::ApplicationController feature_category :devops_reports + urgency :low + def index end end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index c1fa104ffda..f19333d5d57 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -2,6 +2,7 @@ class Admin::UsersController < Admin::ApplicationController include RoutableActions + include SortingHelper before_action :user, except: [:index, :new, :create] before_action :check_impersonation_availability, only: :impersonate @@ -18,7 +19,8 @@ class Admin::UsersController < Admin::ApplicationController @users = User.filter_items(params[:filter]).order_name_asc @users = @users.search(params[:search_query], with_private_emails: true) if params[:search_query].present? @users = users_with_included_associations(@users) - @users = @users.sort_by_attribute(@sort = params[:sort]) + @sort = params[:sort].presence || sort_value_name + @users = @users.sort_by_attribute(@sort) @users = @users.page(params[:page]) @users = @users.without_count if paginate_without_count? end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 8e758c669db..1d17e8aa085 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -111,6 +111,15 @@ class ApplicationController < ActionController::Base render plain: e.message, status: :too_many_requests end + content_security_policy do |p| + next if p.directives.blank? + next unless Gitlab::CurrentSettings.snowplow_enabled? && !Gitlab::CurrentSettings.snowplow_collector_hostname.blank? + + default_connect_src = p.directives['connect-src'] || p.directives['default-src'] + connect_src_values = Array.wrap(default_connect_src) | [Gitlab::CurrentSettings.snowplow_collector_hostname] + p.connect_src(*connect_src_values) + end + def redirect_back_or_default(default: root_path, options: {}) redirect_back(fallback_location: default, **options) end @@ -237,19 +246,19 @@ class ApplicationController < ActionController::Base end def git_not_found! - render "errors/git_not_found.html", layout: "errors", status: :not_found + render template: "errors/git_not_found", formats: :html, layout: "errors", status: :not_found end def render_403 respond_to do |format| - format.html { render "errors/access_denied", layout: "errors", status: :forbidden } + format.html { render template: "errors/access_denied", formats: :html, layout: "errors", status: :forbidden } format.any { head :forbidden } end end def render_404 respond_to do |format| - format.html { render "errors/not_found", layout: "errors", status: :not_found } + format.html { render template: "errors/not_found", formats: :html, layout: "errors", status: :not_found } # Prevent the Rails CSRF protector from thinking a missing .js file is a JavaScript file format.js { render json: '', status: :not_found, content_type: 'application/json' } format.any { head :not_found } diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index ee5caf63703..4bcd1be9f53 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true class AutocompleteController < ApplicationController + include SearchRateLimitable + skip_before_action :authenticate_user!, only: [:users, :award_emojis, :merge_request_target_branches] - before_action :check_email_search_rate_limit!, only: [:users] + before_action :check_search_rate_limit!, only: [:users, :projects] feature_category :users, [:users, :user] feature_category :projects, [:projects] @@ -72,12 +74,6 @@ class AutocompleteController < ApplicationController def target_branch_params params.permit(:group_id, :project_id).select { |_, v| v.present? } end - - def check_email_search_rate_limit! - search_params = Gitlab::Search::Params.new(params) - - check_rate_limit!(:user_email_lookup, scope: [current_user]) if search_params.email_lookup? - end end AutocompleteController.prepend_mod_with('AutocompleteController') diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb index c12ceca9c3b..d9179129983 100644 --- a/app/controllers/clusters/clusters_controller.rb +++ b/app/controllers/clusters/clusters_controller.rb @@ -9,15 +9,25 @@ class Clusters::ClustersController < Clusters::BaseController before_action :generate_gcp_authorize_url, only: [:new] before_action :validate_gcp_token, only: [:new] before_action :gcp_cluster, only: [:new] - before_action :user_cluster, only: [:new] + before_action :user_cluster, only: [:new, :connect] before_action :authorize_read_cluster!, only: [:show, :index] - before_action :authorize_create_cluster!, only: [:new, :authorize_aws_role] + before_action :authorize_create_cluster!, only: [:new, :connect, :authorize_aws_role] before_action :authorize_update_cluster!, only: [:update] before_action :update_applications_status, only: [:cluster_status] + before_action :ensure_feature_enabled!, except: :index helper_method :token_in_session STATUS_POLLING_INTERVAL = 10_000 + AWS_CSP_DOMAINS = %w[https://ec2.ap-east-1.amazonaws.com https://ec2.ap-northeast-1.amazonaws.com https://ec2.ap-northeast-2.amazonaws.com https://ec2.ap-northeast-3.amazonaws.com https://ec2.ap-south-1.amazonaws.com https://ec2.ap-southeast-1.amazonaws.com https://ec2.ap-southeast-2.amazonaws.com https://ec2.ca-central-1.amazonaws.com https://ec2.eu-central-1.amazonaws.com https://ec2.eu-north-1.amazonaws.com https://ec2.eu-west-1.amazonaws.com https://ec2.eu-west-2.amazonaws.com https://ec2.eu-west-3.amazonaws.com https://ec2.me-south-1.amazonaws.com https://ec2.sa-east-1.amazonaws.com https://ec2.us-east-1.amazonaws.com https://ec2.us-east-2.amazonaws.com https://ec2.us-west-1.amazonaws.com https://ec2.us-west-2.amazonaws.com https://ec2.af-south-1.amazonaws.com https://iam.amazonaws.com].freeze + + content_security_policy do |p| + next if p.directives.blank? + + default_connect_src = p.directives['connect-src'] || p.directives['default-src'] + connect_src_values = Array.wrap(default_connect_src) | AWS_CSP_DOMAINS + p.connect_src(*connect_src_values) + end def index @clusters = cluster_list @@ -142,7 +152,7 @@ class Clusters::ClustersController < Clusters::BaseController validate_gcp_token gcp_cluster - render :new, locals: { active_tab: 'add' } + render :connect end end @@ -163,7 +173,17 @@ class Clusters::ClustersController < Clusters::BaseController private + def certificate_based_clusters_enabled? + Feature.enabled?(:certificate_based_clusters, clusterable, default_enabled: :yaml, type: :ops) + end + + def ensure_feature_enabled! + render_404 unless certificate_based_clusters_enabled? + end + def cluster_list + return [] unless certificate_based_clusters_enabled? + finder = ClusterAncestorsFinder.new(clusterable.subject, current_user) clusters = finder.execute diff --git a/app/controllers/concerns/floc_opt_out.rb b/app/controllers/concerns/floc_opt_out.rb index 3039af02bbb..50a52cecda9 100644 --- a/app/controllers/concerns/floc_opt_out.rb +++ b/app/controllers/concerns/floc_opt_out.rb @@ -4,7 +4,7 @@ module FlocOptOut extend ActiveSupport::Concern included do - after_action :set_floc_opt_out_header, unless: :floc_enabled? + before_action :set_floc_opt_out_header, unless: :floc_enabled? end def floc_enabled? diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb index eae087bca4d..ae90bd59d01 100644 --- a/app/controllers/concerns/issuable_actions.rb +++ b/app/controllers/concerns/issuable_actions.rb @@ -17,10 +17,6 @@ module IssuableActions def show respond_to do |format| format.html do - @show_crm_contacts = issuable.is_a?(Issue) && # rubocop:disable Gitlab/ModuleWithInstanceVariables - can?(current_user, :read_crm_contact, issuable.project.group) && - CustomerRelations::Contact.exists_for_group?(issuable.project.group) - @issuable_sidebar = serializer.represent(issuable, serializer: 'sidebar') # rubocop:disable Gitlab/ModuleWithInstanceVariables render 'show' end @@ -43,18 +39,18 @@ module IssuableActions if updated_issuable.is_a?(Spammable) respond_to do |format| format.html do - # NOTE: This redirect is intentionally only performed in the case where the updated - # issuable is a spammable, and intentionally is not performed in the non-spammable case. - # This preserves the legacy behavior of this action. if updated_issuable.valid? + # NOTE: This redirect is intentionally only performed in the case where the valid updated + # issuable is a spammable, and intentionally is not performed below in the + # valid non-spammable case. This preserves the legacy behavior of this action. redirect_to spammable_path else - with_captcha_check_html_format { render :edit } + with_captcha_check_html_format(spammable: spammable) { render :edit } end end format.json do - with_captcha_check_json_format { render_entity_json } + with_captcha_check_json_format(spammable: spammable) { render_entity_json } end end else diff --git a/app/controllers/concerns/issuable_collections_action.rb b/app/controllers/concerns/issuable_collections_action.rb index b68db0e3f9f..96cf6021ea9 100644 --- a/app/controllers/concerns/issuable_collections_action.rb +++ b/app/controllers/concerns/issuable_collections_action.rb @@ -17,7 +17,7 @@ module IssuableCollectionsAction respond_to do |format| format.html - format.atom { render layout: 'xml.atom' } + format.atom { render layout: 'xml' } end end diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb index f716c1f6c2f..0b9024dc3db 100644 --- a/app/controllers/concerns/membership_actions.rb +++ b/app/controllers/concerns/membership_actions.rb @@ -4,17 +4,6 @@ module MembershipActions include MembersPresentation extend ActiveSupport::Concern - def create - create_params = params.permit(:user_ids, :access_level, :expires_at) - result = Members::CreateService.new(current_user, create_params.merge({ source: membershipable, invite_source: "#{plain_source_type}-members-page" })).execute - - if result[:status] == :success - redirect_to members_page_url, notice: _('Users were successfully added.') - else - redirect_to members_page_url, alert: result[:message] - end - end - def update update_params = params.require(root_params_key).permit(:access_level, :expires_at) member = membershipable.members_and_requesters.find(params[:id]) @@ -79,8 +68,8 @@ module MembershipActions notice: _('Your request for access has been queued for review.') else redirect_to polymorphic_path(membershipable), - alert: _("Your request for access could not be processed: %{error_meesage}") % - { error_meesage: access_requester.errors.full_messages.to_sentence } + alert: _("Your request for access could not be processed: %{error_message}") % + { error_message: access_requester.errors.full_messages.to_sentence } end end diff --git a/app/controllers/concerns/product_analytics_tracking.rb b/app/controllers/concerns/product_analytics_tracking.rb new file mode 100644 index 00000000000..03296d6b233 --- /dev/null +++ b/app/controllers/concerns/product_analytics_tracking.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module ProductAnalyticsTracking + include Gitlab::Tracking::Helpers + include RedisTracking + extend ActiveSupport::Concern + + class_methods do + def track_event(*controller_actions, name:, conditions: nil, destinations: [:redis_hll], &block) + custom_conditions = [:trackable_html_request?, *conditions] + + after_action only: controller_actions, if: custom_conditions do + route_events_to(destinations, name, &block) + end + end + end + + private + + def route_events_to(destinations, name, &block) + track_unique_redis_hll_event(name, &block) if destinations.include?(:redis_hll) + + if destinations.include?(:snowplow) && Feature.enabled?(:route_hll_to_snowplow, tracking_namespace_source, default_enabled: :yaml) + Gitlab::Tracking.event(self.class.to_s, name, namespace: tracking_namespace_source, user: current_user) + end + end +end diff --git a/app/controllers/concerns/search_rate_limitable.rb b/app/controllers/concerns/search_rate_limitable.rb new file mode 100644 index 00000000000..a77ebd276b6 --- /dev/null +++ b/app/controllers/concerns/search_rate_limitable.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module SearchRateLimitable + extend ActiveSupport::Concern + + private + + def check_search_rate_limit! + if current_user + check_rate_limit!(:search_rate_limit, scope: [current_user]) + else + check_rate_limit!(:search_rate_limit_unauthenticated, scope: [request.ip]) + end + end +end diff --git a/app/controllers/concerns/sessionless_authentication.rb b/app/controllers/concerns/sessionless_authentication.rb index 1f17f9f4e1b..48daacc09c2 100644 --- a/app/controllers/concerns/sessionless_authentication.rb +++ b/app/controllers/concerns/sessionless_authentication.rb @@ -26,6 +26,9 @@ module SessionlessAuthentication # for every request. If you want the token to work as a # sign in token, you can simply remove store: false. sign_in(user, store: false, message: :sessionless_sign_in) + elsif request_authenticator.can_sign_in_bot?(user) + # we suppress callbacks to avoid redirecting the bot + sign_in(user, store: false, message: :sessionless_sign_in, run_callbacks: false) end end diff --git a/app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb b/app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb index 234c591ffb7..044519004b2 100644 --- a/app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb +++ b/app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb @@ -2,7 +2,6 @@ module SpammableActions::AkismetMarkAsSpamAction extend ActiveSupport::Concern - include SpammableActions::Attributes included do before_action :authorize_submit_spammable!, only: :mark_as_spam @@ -22,7 +21,15 @@ module SpammableActions::AkismetMarkAsSpamAction access_denied! unless current_user.can_admin_all_resources? end + def spammable + # The class extending this module should define the #spammable method to return + # the Spammable model instance via: `alias_method :spammable , <:model_name>` + raise NotImplementedError, "#{self.class} should implement #{__method__}" + end + def spammable_path - raise NotImplementedError, "#{self.class} does not implement #{__method__}" + # The class extending this module should define the #spammable_path method to return + # the route helper pointing to the action to show the Spammable instance + raise NotImplementedError, "#{self.class} should implement #{__method__}" end end diff --git a/app/controllers/concerns/spammable_actions/attributes.rb b/app/controllers/concerns/spammable_actions/attributes.rb deleted file mode 100644 index d7060e47c07..00000000000 --- a/app/controllers/concerns/spammable_actions/attributes.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module SpammableActions - module Attributes - extend ActiveSupport::Concern - - private - - def spammable - raise NotImplementedError, "#{self.class} does not implement #{__method__}" - end - end -end diff --git a/app/controllers/concerns/spammable_actions/captcha_check/common.rb b/app/controllers/concerns/spammable_actions/captcha_check/common.rb index 7c047e02a1d..aaeb6b3ba83 100644 --- a/app/controllers/concerns/spammable_actions/captcha_check/common.rb +++ b/app/controllers/concerns/spammable_actions/captcha_check/common.rb @@ -1,23 +1,25 @@ # frozen_string_literal: true -module SpammableActions::CaptchaCheck - module Common - extend ActiveSupport::Concern +module SpammableActions + module CaptchaCheck + module Common + extend ActiveSupport::Concern - private + private - def with_captcha_check_common(captcha_render_lambda:, &block) - # If the Spammable indicates that CAPTCHA is not necessary (either due to it not being flagged - # as spam, or if spam/captcha is disabled for some reason), then we will go ahead and - # yield to the block containing the action's original behavior, then return. - return yield unless spammable.render_recaptcha? + def with_captcha_check_common(spammable:, captcha_render_lambda:, &block) + # If the Spammable indicates that CAPTCHA is not necessary (either due to it not being flagged + # as spam, or if spam/captcha is disabled for some reason), then we will go ahead and + # yield to the block containing the action's original behavior, then return. + return yield unless spammable.render_recaptcha? - # If we got here, we need to render the CAPTCHA instead of yielding to action's original - # behavior. We will present a CAPTCHA to be solved by executing the lambda which was passed - # as the `captcha_render_lambda:` argument. This lambda contains either the HTML-specific or - # JSON-specific behavior to cause the CAPTCHA modal to be rendered. - Gitlab::Recaptcha.load_configurations! - captcha_render_lambda.call + # If we got here, we need to render the CAPTCHA instead of yielding to action's original + # behavior. We will present a CAPTCHA to be solved by executing the lambda which was passed + # as the `captcha_render_lambda:` argument. This lambda contains either the HTML-specific or + # JSON-specific behavior to cause the CAPTCHA modal to be rendered. + Gitlab::Recaptcha.load_configurations! + captcha_render_lambda.call + end end end end diff --git a/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb b/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb index f687c0fcf2d..b254916cdd6 100644 --- a/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb +++ b/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb @@ -8,7 +8,6 @@ # which supports JSON format should be used instead. module SpammableActions::CaptchaCheck::HtmlFormatActionsSupport extend ActiveSupport::Concern - include SpammableActions::Attributes include SpammableActions::CaptchaCheck::Common included do @@ -17,9 +16,9 @@ module SpammableActions::CaptchaCheck::HtmlFormatActionsSupport private - def with_captcha_check_html_format(&block) + def with_captcha_check_html_format(spammable:, &block) captcha_render_lambda = -> { render :captcha_check } - with_captcha_check_common(captcha_render_lambda: captcha_render_lambda, &block) + with_captcha_check_common(spammable: spammable, captcha_render_lambda: captcha_render_lambda, &block) end # Convert spam/CAPTCHA values from form field params to headers, because all spam-related services diff --git a/app/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support.rb b/app/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support.rb index 0bfea05abc7..4a278a7b233 100644 --- a/app/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support.rb +++ b/app/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support.rb @@ -4,22 +4,27 @@ # In other words, forms handled by actions which use a `respond_to` of `format.js` or `format.json`. # # For example, for all Javascript based form submissions and Vue components which use Apollo and Axios +# which are directly handled by a controller other than `GraphqlController`. For example, issue +# update currently uses this module. +# +# However, requests which directly hit `GraphqlController` will not use this module - the +# `Mutations::SpamProtection` module handles those requests (for example, snippet create/update +# requests) # # If the request is handled by actions via `format.html`, then the corresponding module which # supports HTML format should be used instead. module SpammableActions::CaptchaCheck::JsonFormatActionsSupport extend ActiveSupport::Concern - include SpammableActions::Attributes include SpammableActions::CaptchaCheck::Common include Spam::Concerns::HasSpamActionResponseFields private - def with_captcha_check_json_format(&block) + def with_captcha_check_json_format(spammable:, &block) # NOTE: "409 - Conflict" seems to be the most appropriate HTTP status code for a response # which requires a CAPTCHA to be solved in order for the request to be resubmitted. # https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10 captcha_render_lambda = -> { render json: spam_action_response_fields(spammable), status: :conflict } - with_captcha_check_common(captcha_render_lambda: captcha_render_lambda, &block) + with_captcha_check_common(spammable: spammable, captcha_render_lambda: captcha_render_lambda, &block) end end diff --git a/app/controllers/concerns/spammable_actions/captcha_check/rest_api_actions_support.rb b/app/controllers/concerns/spammable_actions/captcha_check/rest_api_actions_support.rb new file mode 100644 index 00000000000..2ebfa90e6da --- /dev/null +++ b/app/controllers/concerns/spammable_actions/captcha_check/rest_api_actions_support.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +# This module should be included to support CAPTCHA check for REST API actions via Grape. +# +# If the request is directly handled by a controller action, then the corresponding module which +# supports HTML or JSON formats should be used instead. +module SpammableActions::CaptchaCheck::RestApiActionsSupport + extend ActiveSupport::Concern + include SpammableActions::CaptchaCheck::Common + include Spam::Concerns::HasSpamActionResponseFields + + private + + def with_captcha_check_rest_api(spammable:, &block) + # In the case of the REST API, the request is handled by Grape, so if there is a spam-related + # error, we don't render directly, instead we will pass the error message and other necessary + # fields to the Grape api error helper for it to handle. + captcha_render_lambda = -> do + fields = spam_action_response_fields(spammable) + + fields.delete :spam + # NOTE: "409 - Conflict" seems to be the most appropriate HTTP status code for a response + # which requires a CAPTCHA to be solved in order for the request to be resubmitted. + # https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.10 + status = 409 + + # NOTE: This nested 'error' key may not be consistent with all other API error responses, + # because they are not currently consistent across different API endpoints + # and models. Some (snippets) will nest errors in an errors key like this, + # while others (issues) will return the model's errors hash without an errors key, + # while still others just return a plain string error. + # See https://gitlab.com/groups/gitlab-org/-/epics/5527#revisit-inconsistent-shape-of-error-responses-in-rest-api + fields[:message] = { error: spammable.errors.full_messages.to_sentence } + render_structured_api_error!(fields, status) + end + + with_captcha_check_common(spammable: spammable, captcha_render_lambda: captcha_render_lambda, &block) + end +end diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb index e1bfe92f61b..c9b6e8923fe 100644 --- a/app/controllers/concerns/uploads_actions.rb +++ b/app/controllers/concerns/uploads_actions.rb @@ -143,7 +143,7 @@ module UploadsActions end def bypass_auth_checks_on_uploads? - if ::Feature.enabled?(:enforce_auth_checks_on_uploads, default_enabled: :yaml) + if ::Feature.enabled?(:enforce_auth_checks_on_uploads, project, default_enabled: :yaml) false else action_name == 'show' && embeddable? diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index 0074bcac360..4d6c7a63516 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -23,7 +23,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController end format.atom do load_events - render layout: 'xml.atom' + render layout: 'xml' end format.json do render json: { diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index f94da77609f..f25cc1bbc32 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -20,10 +20,6 @@ class DashboardController < Dashboard::ApplicationController urgency :low, [:merge_requests] - before_action only: [:merge_requests] do - push_frontend_feature_flag(:mr_attention_requests, default_enabled: :yaml) - end - def activity respond_to do |format| format.html @@ -71,10 +67,15 @@ class DashboardController < Dashboard::ApplicationController end def check_filters_presence! - no_scalar_filters_set = finder_type.scalar_params.none? { |k| params.key?(k) } - no_array_filters_set = finder_type.array_params.none? { |k, _| params.key?(k) } + no_scalar_filters_set = finder_type.scalar_params.none? { |k| params[k].present? } + no_array_filters_set = finder_type.array_params.none? { |k, _| params[k].present? } + + # The `in` param is a modifier of `search`. If it's present while the `search` + # param isn't, the finder won't use the `in` param. We consider this as a no + # filter scenario. + no_search_filter_set = params[:in].present? && params[:search].blank? - @no_filters_set = no_scalar_filters_set && no_array_filters_set + @no_filters_set = (no_scalar_filters_set && no_array_filters_set) || no_search_filter_set return unless @no_filters_set diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb index f9c875b80b2..bf72ade32d0 100644 --- a/app/controllers/groups/application_controller.rb +++ b/app/controllers/groups/application_controller.rb @@ -82,6 +82,10 @@ class Groups::ApplicationController < ApplicationController def has_project_list? false end + + def validate_root_group! + render_404 unless group.root? + end end Groups::ApplicationController.prepend_mod_with('Groups::ApplicationController') diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb index 6fac6fcf426..641b3adb12b 100644 --- a/app/controllers/groups/boards_controller.rb +++ b/app/controllers/groups/boards_controller.rb @@ -7,7 +7,6 @@ class Groups::BoardsController < Groups::ApplicationController before_action :assign_endpoint_vars before_action do - push_frontend_feature_flag(:issue_boards_filtered_search, group, default_enabled: :yaml) push_frontend_feature_flag(:board_multi_select, group, default_enabled: :yaml) push_frontend_feature_flag(:iteration_cadences, group, default_enabled: :yaml) experiment(:prominent_create_board_btn, subject: current_user) do |e| diff --git a/app/controllers/groups/clusters_controller.rb b/app/controllers/groups/clusters_controller.rb index 666a96d6fc0..2fe9faa252f 100644 --- a/app/controllers/groups/clusters_controller.rb +++ b/app/controllers/groups/clusters_controller.rb @@ -3,6 +3,7 @@ class Groups::ClustersController < Clusters::ClustersController include ControllerWithCrossProjectAccessCheck + before_action :ensure_feature_enabled! prepend_before_action :group requires_cross_project_access diff --git a/app/controllers/groups/crm/contacts_controller.rb b/app/controllers/groups/crm/contacts_controller.rb index f00f4d1df25..b59e20d9cea 100644 --- a/app/controllers/groups/crm/contacts_controller.rb +++ b/app/controllers/groups/crm/contacts_controller.rb @@ -3,6 +3,7 @@ class Groups::Crm::ContactsController < Groups::ApplicationController feature_category :team_planning + before_action :validate_root_group! before_action :authorize_read_crm_contact! def new diff --git a/app/controllers/groups/crm/organizations_controller.rb b/app/controllers/groups/crm/organizations_controller.rb index ab720f490be..f8536b4f538 100644 --- a/app/controllers/groups/crm/organizations_controller.rb +++ b/app/controllers/groups/crm/organizations_controller.rb @@ -3,6 +3,7 @@ class Groups::Crm::OrganizationsController < Groups::ApplicationController feature_category :team_planning + before_action :validate_root_group! before_action :authorize_read_crm_organization! def new diff --git a/app/controllers/groups/deploy_tokens_controller.rb b/app/controllers/groups/deploy_tokens_controller.rb index 79152bf2695..9ef22aa33dc 100644 --- a/app/controllers/groups/deploy_tokens_controller.rb +++ b/app/controllers/groups/deploy_tokens_controller.rb @@ -6,8 +6,7 @@ class Groups::DeployTokensController < Groups::ApplicationController feature_category :continuous_delivery def revoke - @token = @group.deploy_tokens.find(params[:id]) - @token.revoke! + Groups::DeployTokens::RevokeService.new(@group, current_user, params).execute redirect_to group_settings_repository_path(@group, anchor: 'js-deploy-tokens') end diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index 6e59f159636..ece1083d4d1 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -16,7 +16,7 @@ class Groups::GroupMembersController < Groups::ApplicationController before_action :authorize_admin_group_member!, except: admin_not_required_endpoints skip_before_action :check_two_factor_requirement, only: :leave - skip_cross_project_access_check :index, :create, :update, :destroy, :request_access, + skip_cross_project_access_check :index, :update, :destroy, :request_access, :approve_access_request, :leave, :resend_invite, :override @@ -26,8 +26,6 @@ class Groups::GroupMembersController < Groups::ApplicationController @sort = params[:sort].presence || sort_value_name if can?(current_user, :admin_group_member, @group) - @skip_groups = @group.related_group_ids - @invited_members = invited_members @invited_members = @invited_members.search_invite_email(params[:search_invited]) if params[:search_invited].present? @invited_members = present_invited_members(@invited_members) @@ -38,8 +36,6 @@ class Groups::GroupMembersController < Groups::ApplicationController @requesters = present_members( AccessRequestsFinder.new(@group).execute(current_user) ) - - @group_member = @group.group_members.new end # MembershipActions concern diff --git a/app/controllers/groups/harbor/repositories_controller.rb b/app/controllers/groups/harbor/repositories_controller.rb new file mode 100644 index 00000000000..364607f9b20 --- /dev/null +++ b/app/controllers/groups/harbor/repositories_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Groups + module Harbor + class RepositoriesController < Groups::ApplicationController + feature_category :integrations + + before_action :harbor_registry_enabled! + before_action do + push_frontend_feature_flag(:harbor_registry_integration) + end + + def show + render :index + end + + private + + def harbor_registry_enabled! + render_404 unless Feature.enabled?(:harbor_registry_integration) + end + end + end +end diff --git a/app/controllers/groups/releases_controller.rb b/app/controllers/groups/releases_controller.rb index 6a42f30b847..db5385ecc71 100644 --- a/app/controllers/groups/releases_controller.rb +++ b/app/controllers/groups/releases_controller.rb @@ -15,11 +15,17 @@ module Groups private def releases - ReleasesFinder - .new(@group, current_user, { include_subgroups: true }) - .execute(preload: false) - .page(params[:page]) - .per(30) + if Feature.enabled?(:group_releases_finder_inoperator) + Releases::GroupReleasesFinder + .new(@group, current_user, { include_subgroups: true, page: params[:page], per: 30 }) + .execute(preload: false) + else + ReleasesFinder + .new(@group, current_user, { include_subgroups: true }) + .execute(preload: false) + .page(params[:page]) + .per(30) + end end end end diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb index b194aeff80d..dabef978ee1 100644 --- a/app/controllers/groups/runners_controller.rb +++ b/app/controllers/groups/runners_controller.rb @@ -24,7 +24,7 @@ class Groups::RunnersController < Groups::ApplicationController end def update - if Ci::UpdateRunnerService.new(@runner).update(runner_params) + if Ci::Runners::UpdateRunnerService.new(@runner).update(runner_params) redirect_to group_runner_path(@group, @runner), notice: _('Runner was successfully updated.') else render 'edit' @@ -32,17 +32,17 @@ class Groups::RunnersController < Groups::ApplicationController end def destroy - if @runner.belongs_to_more_than_one_project? - redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: :found, alert: _('Runner was not deleted because it is assigned to multiple projects.') - else - Ci::UnregisterRunnerService.new(@runner).execute + if can?(current_user, :delete_runner, @runner) + Ci::Runners::UnregisterRunnerService.new(@runner, current_user).execute redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: :found + else + redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: :found, alert: _('Runner cannot be deleted, please contact your administrator.') end end def resume - if Ci::UpdateRunnerService.new(@runner).update(active: true) + if Ci::Runners::UpdateRunnerService.new(@runner).update(active: true) redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), notice: _('Runner was successfully updated.') else redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), alert: _('Runner was not updated.') @@ -50,7 +50,7 @@ class Groups::RunnersController < Groups::ApplicationController end def pause - if Ci::UpdateRunnerService.new(@runner).update(active: false) + if Ci::Runners::UpdateRunnerService.new(@runner).update(active: false) redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), notice: _('Runner was successfully updated.') else redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), alert: _('Runner was not updated.') diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb index a290ef9b5e7..9b9e3f7b0bc 100644 --- a/app/controllers/groups/settings/ci_cd_controller.rb +++ b/app/controllers/groups/settings/ci_cd_controller.rb @@ -36,7 +36,7 @@ module Groups end def reset_registration_token - @group.reset_runners_token! + ::Ci::Runners::ResetRegistrationTokenService.new(@group, current_user).execute flash[:notice] = _('GroupSettings|New runners registration token has been generated!') redirect_to group_settings_ci_cd_path diff --git a/app/controllers/groups/settings/integrations_controller.rb b/app/controllers/groups/settings/integrations_controller.rb index 0a63c3d304b..ec64e75a68e 100644 --- a/app/controllers/groups/settings/integrations_controller.rb +++ b/app/controllers/groups/settings/integrations_controller.rb @@ -7,6 +7,10 @@ module Groups before_action :authorize_admin_group! + before_action do + push_frontend_feature_flag(:integration_form_sections, group, default_enabled: :yaml) + end + feature_category :integrations layout 'group_settings' diff --git a/app/controllers/groups/uploads_controller.rb b/app/controllers/groups/uploads_controller.rb index 387f7be56cd..49249f87d31 100644 --- a/app/controllers/groups/uploads_controller.rb +++ b/app/controllers/groups/uploads_controller.rb @@ -4,7 +4,7 @@ class Groups::UploadsController < Groups::ApplicationController include UploadsActions include WorkhorseRequest - skip_before_action :group, if: -> { bypass_auth_checks_on_uploads? } + skip_before_action :group, if: -> { action_name == 'show' && embeddable? } before_action :authorize_upload_file!, only: [:create, :authorize] before_action :verify_workhorse_api!, only: [:authorize] diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 12af76efe0d..b53d9b1be04 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -212,7 +212,7 @@ class GroupsController < Groups::ApplicationController def issues return super if !html_request? || Feature.disabled?(:vue_issues_list, group, default_enabled: :yaml) - @has_issues = IssuesFinder.new(current_user, group_id: group.id).execute + @has_issues = IssuesFinder.new(current_user, group_id: group.id, include_subgroups: true).execute .non_archived .exists? @@ -235,7 +235,7 @@ class GroupsController < Groups::ApplicationController def render_details_view_atom load_events - render layout: 'xml.atom', template: 'groups/show' + render layout: 'xml', template: 'groups/show' end # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/controllers/jira_connect/events_controller.rb b/app/controllers/jira_connect/events_controller.rb index 1ea0a92662b..327192857f6 100644 --- a/app/controllers/jira_connect/events_controller.rb +++ b/app/controllers/jira_connect/events_controller.rb @@ -7,11 +7,13 @@ class JiraConnect::EventsController < JiraConnect::ApplicationController before_action :verify_asymmetric_atlassian_jwt! def installed - return head :ok if current_jira_installation + unless Feature.enabled?(:jira_connect_installation_update, default_enabled: :yaml) + return head :ok if current_jira_installation + end - installation = JiraConnectInstallation.new(event_params) + success = current_jira_installation ? update_installation : create_installation - if installation.save + if success head :ok else head :unprocessable_entity @@ -28,8 +30,24 @@ class JiraConnect::EventsController < JiraConnect::ApplicationController private - def event_params - params.permit(:clientKey, :sharedSecret, :baseUrl).transform_keys(&:underscore) + def create_installation + JiraConnectInstallation.new(create_params).save + end + + def update_installation + current_jira_installation.update(update_params) + end + + def create_params + transformed_params.permit(:client_key, :shared_secret, :base_url) + end + + def update_params + transformed_params.permit(:shared_secret, :base_url) + end + + def transformed_params + @transformed_params ||= params.transform_keys(&:underscore) end def verify_asymmetric_atlassian_jwt! @@ -43,7 +61,7 @@ class JiraConnect::EventsController < JiraConnect::ApplicationController def jwt_verification_claims { aud: jira_connect_base_url(protocol: 'https'), - iss: event_params[:client_key], + iss: transformed_params[:client_key], qsh: Atlassian::Jwt.create_query_string_hash(request.url, request.method, jira_connect_base_url) } end diff --git a/app/controllers/jira_connect/oauth_callbacks_controller.rb b/app/controllers/jira_connect/oauth_callbacks_controller.rb new file mode 100644 index 00000000000..f603a563402 --- /dev/null +++ b/app/controllers/jira_connect/oauth_callbacks_controller.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +# This controller's role is to serve as a landing page +# that users get redirected to after installing and authenticating +# The GitLab.com for Jira App (https://marketplace.atlassian.com/apps/1221011/gitlab-com-for-jira-cloud) +# +class JiraConnect::OauthCallbacksController < ApplicationController + feature_category :integrations + + def index; end +end diff --git a/app/controllers/jira_connect/subscriptions_controller.rb b/app/controllers/jira_connect/subscriptions_controller.rb index fcd95c7942c..ec6ba07a125 100644 --- a/app/controllers/jira_connect/subscriptions_controller.rb +++ b/app/controllers/jira_connect/subscriptions_controller.rb @@ -16,6 +16,10 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController p.style_src(*style_src_values) end + before_action do + push_frontend_feature_flag(:jira_connect_oauth, @user, default_enabled: :yaml) + end + before_action :allow_rendering_in_iframe, only: :index before_action :verify_qsh_claim!, only: :index before_action :authenticate_user!, only: :create diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 46738651960..d57a293ab4d 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -7,7 +7,7 @@ class ProfilesController < Profiles::ApplicationController before_action :user before_action :authorize_change_username!, only: :update_username before_action only: :update_username do - check_rate_limit!(:profile_update_username, scope: current_user) if Feature.enabled?(:rate_limit_profile_update_username, default_enabled: :yaml) + check_rate_limit!(:profile_update_username, scope: current_user) end skip_before_action :require_email, only: [:show, :update] before_action do diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 7a03e7b84b7..62233c8c3c9 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -24,11 +24,14 @@ class Projects::ApplicationController < ApplicationController return unless params[:project_id] || params[:id] path = File.join(params[:namespace_id], params[:project_id] || params[:id]) - auth_proc = ->(project) { !project.pending_delete? } @project = find_routable!(Project, path, request.fullpath, extra_authorization_proc: auth_proc) end + def auth_proc + ->(project) { !project.pending_delete? } + end + def build_canonical_path(project) params[:namespace_id] = project.namespace.to_param params[:project_id] = project.to_param @@ -89,3 +92,5 @@ class Projects::ApplicationController < ApplicationController return render_404 unless @project.feature_available?(:issues, current_user) end end + +Projects::ApplicationController.prepend_mod_with('Projects::ApplicationController') diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index b30ef7506aa..26a7b5662be 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -35,7 +35,6 @@ class Projects::BlobController < Projects::ApplicationController before_action :editor_variables, except: [:show, :preview, :diff] before_action :validate_diff_params, only: :diff before_action :set_last_commit_sha, only: [:edit, :update] - before_action :track_experiment, only: :create track_redis_hll_event :create, :update, name: 'g_edit_by_sfe' @@ -45,7 +44,6 @@ class Projects::BlobController < Projects::ApplicationController before_action do push_frontend_feature_flag(:refactor_blob_viewer, @project, default_enabled: :yaml) push_frontend_feature_flag(:highlight_js, @project, default_enabled: :yaml) - push_frontend_feature_flag(:consolidated_edit_button, @project, default_enabled: :yaml) push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) end @@ -55,7 +53,7 @@ class Projects::BlobController < Projects::ApplicationController def create create_commit(Files::CreateService, success_notice: _("The file has been successfully created."), - success_path: -> { create_success_path }, + success_path: -> { project_blob_path(@project, File.join(@branch_name, @file_path)) }, failure_view: :new, failure_path: project_new_blob_path(@project, @ref)) end @@ -283,20 +281,6 @@ class Projects::BlobController < Projects::ApplicationController def visitor_id current_user&.id end - - def create_success_path - if params[:code_quality_walkthrough] - project_pipelines_path(@project, code_quality_walkthrough: true) - else - project_blob_path(@project, File.join(@branch_name, @file_path)) - end - end - - def track_experiment - return unless params[:code_quality_walkthrough] - - experiment(:code_quality_walkthrough, namespace: @project.root_ancestor).track(:commit_created) - end end Projects::BlobController.prepend_mod diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb index 0170cff6160..c44a0830e2e 100644 --- a/app/controllers/projects/boards_controller.rb +++ b/app/controllers/projects/boards_controller.rb @@ -7,7 +7,6 @@ class Projects::BoardsController < Projects::ApplicationController before_action :check_issues_available! before_action :assign_endpoint_vars before_action do - push_frontend_feature_flag(:issue_boards_filtered_search, project&.group, default_enabled: :yaml) push_frontend_feature_flag(:board_multi_select, project, default_enabled: :yaml) push_frontend_feature_flag(:iteration_cadences, project&.group, default_enabled: :yaml) experiment(:prominent_create_board_btn, subject: current_user) do |e| diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb index c5f6ed1c105..61e8e5b015a 100644 --- a/app/controllers/projects/builds_controller.rb +++ b/app/controllers/projects/builds_controller.rb @@ -5,6 +5,9 @@ class Projects::BuildsController < Projects::ApplicationController feature_category :continuous_integration + urgency :high, [:index, :show] + urgency :low, [:raw] + def index redirect_to project_jobs_path(project) end diff --git a/app/controllers/projects/ci/pipeline_editor_controller.rb b/app/controllers/projects/ci/pipeline_editor_controller.rb index 6f12e3940dd..8c6e8f0e126 100644 --- a/app/controllers/projects/ci/pipeline_editor_controller.rb +++ b/app/controllers/projects/ci/pipeline_editor_controller.rb @@ -2,7 +2,6 @@ class Projects::Ci::PipelineEditorController < Projects::ApplicationController before_action :check_can_collaborate! - before_action :setup_walkthrough_experiment, only: :show before_action do push_frontend_feature_flag(:schema_linting, @project, default_enabled: :yaml) end @@ -19,11 +18,4 @@ class Projects::Ci::PipelineEditorController < Projects::ApplicationController def check_can_collaborate! render_404 unless can_collaborate_with_project?(@project) end - - def setup_walkthrough_experiment - experiment(:pipeline_editor_walkthrough, namespace: @project.namespace, sticky_to: current_user) do |e| - e.candidate {} - e.publish_to_database - end - end end diff --git a/app/controllers/projects/ci/secure_files_controller.rb b/app/controllers/projects/ci/secure_files_controller.rb new file mode 100644 index 00000000000..5141d0188b0 --- /dev/null +++ b/app/controllers/projects/ci/secure_files_controller.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class Projects::Ci::SecureFilesController < Projects::ApplicationController + before_action :authorize_read_secure_files! + + feature_category :pipeline_authoring + + def show + end +end diff --git a/app/controllers/projects/cluster_agents_controller.rb b/app/controllers/projects/cluster_agents_controller.rb index 84bb01ee266..282b9ef1fb7 100644 --- a/app/controllers/projects/cluster_agents_controller.rb +++ b/app/controllers/projects/cluster_agents_controller.rb @@ -3,10 +3,6 @@ class Projects::ClusterAgentsController < Projects::ApplicationController before_action :authorize_can_read_cluster_agent! - before_action do - push_frontend_feature_flag(:cluster_vulnerabilities, project, default_enabled: :yaml) - end - feature_category :kubernetes_management def show diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 0ce0b8b8895..0c26b402876 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -164,6 +164,7 @@ class Projects::CommitController < Projects::ApplicationController opts = diff_options opts[:ignore_whitespace_change] = true if params[:format] == 'diff' + opts[:use_extra_viewer_as_main] = false @diffs = commit.diffs(opts) @notes_count = commit.notes.count diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index 82a13b60b13..60b8e45f5be 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -30,7 +30,7 @@ class Projects::CommitsController < Projects::ApplicationController respond_to do |format| format.html - format.atom { render layout: 'xml.atom' } + format.atom { render layout: 'xml' } format.json do pager_json( diff --git a/app/controllers/projects/deploy_tokens_controller.rb b/app/controllers/projects/deploy_tokens_controller.rb index 3c890bbafdf..42c2d8b17f1 100644 --- a/app/controllers/projects/deploy_tokens_controller.rb +++ b/app/controllers/projects/deploy_tokens_controller.rb @@ -12,3 +12,5 @@ class Projects::DeployTokensController < Projects::ApplicationController redirect_to project_settings_repository_path(project, anchor: 'js-deploy-tokens') end end + +Projects::DeployTokensController.prepend_mod diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 84ebdcd9364..eabc048e341 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -29,13 +29,14 @@ class Projects::EnvironmentsController < Projects::ApplicationController feature_category :continuous_delivery def index - @environments = project.environments - .with_state(params[:scope] || :available) @project = ProjectPresenter.new(project, current_user: current_user) respond_to do |format| format.html format.json do + @environments = project.environments + .with_state(params[:scope] || :available) + Gitlab::PollingInterval.set_header(response, interval: 3_000) environments_count_by_state = project.environments.count_by_state @@ -52,14 +53,15 @@ class Projects::EnvironmentsController < Projects::ApplicationController # Returns all environments for a given folder # rubocop: disable CodeReuse/ActiveRecord def folder - folder_environments = project.environments.where(environment_type: params[:id]) - @environments = folder_environments.with_state(params[:scope] || :available) - .order(:name) @folder = params[:id] respond_to do |format| format.html format.json do + folder_environments = project.environments.where(environment_type: params[:id]) + @environments = folder_environments.with_state(params[:scope] || :available) + .order(:name) + render json: { environments: serialize_environments(request, response), available_count: folder_environments.available.count, diff --git a/app/controllers/projects/error_tracking_controller.rb b/app/controllers/projects/error_tracking_controller.rb index 8700d3c2198..06383d26133 100644 --- a/app/controllers/projects/error_tracking_controller.rb +++ b/app/controllers/projects/error_tracking_controller.rb @@ -6,6 +6,10 @@ class Projects::ErrorTrackingController < Projects::ErrorTracking::BaseControlle before_action :authorize_read_sentry_issue! before_action :set_issue_id, only: :details + before_action only: [:index] do + push_frontend_feature_flag(:integrated_error_tracking, project) + end + def index respond_to do |format| format.html @@ -75,7 +79,7 @@ class Projects::ErrorTrackingController < Projects::ErrorTracking::BaseControlle end def list_issues_params - params.permit(:search_term, :sort, :cursor, :issue_status) + params.permit(:search_term, :sort, :cursor, :issue_status).merge(tracking_event: :error_tracking_view_list) end def issue_update_params @@ -83,7 +87,7 @@ class Projects::ErrorTrackingController < Projects::ErrorTracking::BaseControlle end def issue_details_params - params.permit(:issue_id) + params.permit(:issue_id).merge(tracking_event: :error_tracking_view_details) end def set_issue_id diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index 475c41eec9c..3208a5076e7 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -17,10 +17,6 @@ class Projects::ForksController < Projects::ApplicationController feature_category :source_code_management urgency :low, [:index] - before_action do - push_frontend_feature_flag(:fork_project_form, @project, default_enabled: :yaml) - end - def index @sort = forks_params[:sort] @@ -54,9 +50,7 @@ class Projects::ForksController < Projects::ApplicationController format.json do namespaces = load_namespaces_with_associations - [project.namespace] - namespaces = [current_user.namespace] + namespaces if - Feature.enabled?(:fork_project_form, project, default_enabled: :yaml) && - can_fork_to?(current_user.namespace) + namespaces = [current_user.namespace] + namespaces if can_fork_to?(current_user.namespace) render json: { namespaces: ForkNamespaceSerializer.new.represent( diff --git a/app/controllers/projects/google_cloud/base_controller.rb b/app/controllers/projects/google_cloud/base_controller.rb index f4a773a62f6..f293ec752ab 100644 --- a/app/controllers/projects/google_cloud/base_controller.rb +++ b/app/controllers/projects/google_cloud/base_controller.rb @@ -10,18 +10,25 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController private def admin_project_google_cloud! - access_denied! unless can?(current_user, :admin_project_google_cloud, project) + unless can?(current_user, :admin_project_google_cloud, project) + track_event('admin_project_google_cloud!', 'access_denied', 'invalid_user') + access_denied! + end end def google_oauth2_enabled! config = Gitlab::Auth::OAuth::Provider.config_for('google_oauth2') if config.app_id.blank? || config.app_secret.blank? + track_event('google_oauth2_enabled!', 'access_denied', { reason: 'google_oauth2_not_configured', config: config }) access_denied! 'This GitLab instance not configured for Google Oauth2.' end end def feature_flag_enabled! - access_denied! unless Feature.enabled?(:incubation_5mp_google_cloud, project) + unless Feature.enabled?(:incubation_5mp_google_cloud) + track_event('feature_flag_enabled!', 'access_denied', 'feature_flag_not_enabled') + access_denied! + end end def validate_gcp_token! @@ -53,9 +60,21 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at] end - def handle_gcp_error(error, project) - Gitlab::ErrorTracking.track_exception(error, project_id: project.id) + def handle_gcp_error(action, error) + track_event(action, 'gcp_error', error) @js_data = { screen: 'gcp_error', error: error.to_s }.to_json render status: :unauthorized, template: 'projects/google_cloud/errors/gcp_error' end + + def track_event(action, label, property) + options = { label: label, project: project, user: current_user } + + if property.is_a?(String) + options[:property] = property + else + options[:extra] = property + end + + Gitlab::Tracking.event('Projects::GoogleCloud', action, **options) + end end diff --git a/app/controllers/projects/google_cloud/deployments_controller.rb b/app/controllers/projects/google_cloud/deployments_controller.rb index 1941eb8a5f9..4867d344c5a 100644 --- a/app/controllers/projects/google_cloud/deployments_controller.rb +++ b/app/controllers/projects/google_cloud/deployments_controller.rb @@ -9,6 +9,7 @@ class Projects::GoogleCloud::DeploymentsController < Projects::GoogleCloud::Base .new(project, current_user, params).execute if enable_cloud_run_response[:status] == :error + track_event('deployments#cloud_run', 'enable_cloud_run_error', enable_cloud_run_response) flash[:error] = enable_cloud_run_response[:message] redirect_to project_google_cloud_index_path(project) else @@ -17,15 +18,17 @@ class Projects::GoogleCloud::DeploymentsController < Projects::GoogleCloud::Base .new(project, current_user, params).execute if generate_pipeline_response[:status] == :error + track_event('deployments#cloud_run', 'generate_pipeline_error', generate_pipeline_response) flash[:error] = 'Failed to generate pipeline' redirect_to project_google_cloud_index_path(project) else cloud_run_mr_params = cloud_run_mr_params(generate_pipeline_response[:branch_name]) + track_event('deployments#cloud_run', 'cloud_run_success', cloud_run_mr_params) redirect_to project_new_merge_request_path(project, merge_request: cloud_run_mr_params) end end rescue Google::Apis::ClientError => error - handle_gcp_error(error, project) + handle_gcp_error('deployments#cloud_run', error) end def cloud_storage diff --git a/app/controllers/projects/google_cloud/gcp_regions_controller.rb b/app/controllers/projects/google_cloud/gcp_regions_controller.rb new file mode 100644 index 00000000000..beeb91cfd80 --- /dev/null +++ b/app/controllers/projects/google_cloud/gcp_regions_controller.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class Projects::GoogleCloud::GcpRegionsController < Projects::GoogleCloud::BaseController + # filtered list of GCP cloud run locations... + # ...that have domain mapping available + # Source https://cloud.google.com/run/docs/locations 2022-01-30 + AVAILABLE_REGIONS = %w[asia-east1 asia-northeast1 asia-southeast1 europe-north1 europe-west1 europe-west4 us-central1 us-east1 us-east4 us-west1].freeze + + def index + @google_cloud_path = project_google_cloud_index_path(project) + params = { per_page: 50 } + branches = BranchesFinder.new(project.repository, params).execute(gitaly_pagination: true) + tags = TagsFinder.new(project.repository, params).execute(gitaly_pagination: true) + refs = (branches + tags).map(&:name) + js_data = { + screen: 'gcp_regions_form', + availableRegions: AVAILABLE_REGIONS, + refs: refs, + cancelPath: project_google_cloud_index_path(project) + } + @js_data = js_data.to_json + track_event('gcp_regions#index', 'form_render', js_data) + end + + def create + permitted_params = params.permit(:ref, :gcp_region) + response = GoogleCloud::GcpRegionAddOrReplaceService.new(project).execute(permitted_params[:ref], permitted_params[:gcp_region]) + track_event('gcp_regions#create', 'form_submit', response) + redirect_to project_google_cloud_index_path(project), notice: _('GCP region configured') + end +end diff --git a/app/controllers/projects/google_cloud/revoke_oauth_controller.rb b/app/controllers/projects/google_cloud/revoke_oauth_controller.rb new file mode 100644 index 00000000000..03d1474707b --- /dev/null +++ b/app/controllers/projects/google_cloud/revoke_oauth_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class Projects::GoogleCloud::RevokeOauthController < Projects::GoogleCloud::BaseController + before_action :validate_gcp_token! + + def create + google_api_client = GoogleApi::CloudPlatform::Client.new(token_in_session, nil) + response = google_api_client.revoke_authorizations + + if response.success? + status = 'success' + redirect_message = { notice: s_('GoogleCloud|Google OAuth2 token revocation requested') } + else + status = 'failed' + redirect_message = { alert: s_('GoogleCloud|Google OAuth2 token revocation request failed') } + end + + session.delete(GoogleApi::CloudPlatform::Client.session_key_for_token) + track_event('revoke_oauth#create', 'create', status) + + redirect_to project_google_cloud_index_path(project), redirect_message + end +end diff --git a/app/controllers/projects/google_cloud/service_accounts_controller.rb b/app/controllers/projects/google_cloud/service_accounts_controller.rb index b5f2b658235..5d8b2030d5c 100644 --- a/app/controllers/projects/google_cloud/service_accounts_controller.rb +++ b/app/controllers/projects/google_cloud/service_accounts_controller.rb @@ -10,30 +10,41 @@ class Projects::GoogleCloud::ServiceAccountsController < Projects::GoogleCloud:: if gcp_projects.empty? @js_data = { screen: 'no_gcp_projects' }.to_json + track_event('service_accounts#index', 'form_error', 'no_gcp_projects') render status: :unauthorized, template: 'projects/google_cloud/errors/no_gcp_projects' else - @js_data = { + params = { per_page: 50 } + branches = BranchesFinder.new(project.repository, params).execute(gitaly_pagination: true) + tags = TagsFinder.new(project.repository, params).execute(gitaly_pagination: true) + refs = (branches + tags).map(&:name) + js_data = { screen: 'service_accounts_form', gcpProjects: gcp_projects, - environments: project.environments, + refs: refs, cancelPath: project_google_cloud_index_path(project) - }.to_json + } + @js_data = js_data.to_json + + track_event('service_accounts#index', 'form_success', js_data) end rescue Google::Apis::ClientError => error - handle_gcp_error(error, project) + handle_gcp_error('service_accounts#index', error) end def create + permitted_params = params.permit(:gcp_project, :ref) + response = GoogleCloud::CreateServiceAccountsService.new( project, current_user, google_oauth2_token: token_in_session, - gcp_project_id: params[:gcp_project], - environment_name: params[:environment] + gcp_project_id: permitted_params[:gcp_project], + environment_name: permitted_params[:ref] ).execute + track_event('service_accounts#create', 'form_submit', response) redirect_to project_google_cloud_index_path(project), notice: response.message rescue Google::Apis::ClientError, Google::Apis::ServerError, Google::Apis::AuthorizationError => error - handle_gcp_error(error, project) + handle_gcp_error('service_accounts#create', error) end end diff --git a/app/controllers/projects/google_cloud_controller.rb b/app/controllers/projects/google_cloud_controller.rb index 206a8c7e391..49bb4bec859 100644 --- a/app/controllers/projects/google_cloud_controller.rb +++ b/app/controllers/projects/google_cloud_controller.rb @@ -1,14 +1,34 @@ # frozen_string_literal: true class Projects::GoogleCloudController < Projects::GoogleCloud::BaseController + GCP_REGION_CI_VAR_KEY = 'GCP_REGION' + def index - @js_data = { + js_data = { screen: 'home', serviceAccounts: GoogleCloud::ServiceAccountsService.new(project).find_for_project, createServiceAccountUrl: project_google_cloud_service_accounts_path(project), enableCloudRunUrl: project_google_cloud_deployments_cloud_run_path(project), enableCloudStorageUrl: project_google_cloud_deployments_cloud_storage_path(project), - emptyIllustrationUrl: ActionController::Base.helpers.image_path('illustrations/pipelines_empty.svg') - }.to_json + emptyIllustrationUrl: ActionController::Base.helpers.image_path('illustrations/pipelines_empty.svg'), + configureGcpRegionsUrl: project_google_cloud_gcp_regions_path(project), + gcpRegions: gcp_regions, + revokeOauthUrl: revoke_oauth_url + } + @js_data = js_data.to_json + track_event('google_cloud#index', 'index', js_data) + end + + private + + def gcp_regions + list = ::Ci::VariablesFinder.new(project, { key: GCP_REGION_CI_VAR_KEY }).execute + list.map { |variable| { gcp_region: variable.value, environment: variable.environment_scope } } + end + + def revoke_oauth_url + google_token_valid = GoogleApi::CloudPlatform::Client.new(token_in_session, nil) + .validate_token(expires_at_in_session) + google_token_valid ? project_google_cloud_revoke_oauth_index_path(project) : nil end end diff --git a/app/controllers/projects/harbor/application_controller.rb b/app/controllers/projects/harbor/application_controller.rb new file mode 100644 index 00000000000..e6e694783fa --- /dev/null +++ b/app/controllers/projects/harbor/application_controller.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Projects + module Harbor + class ApplicationController < Projects::ApplicationController + layout 'project' + + before_action :harbor_registry_enabled! + before_action do + push_frontend_feature_flag(:harbor_registry_integration) + end + + feature_category :integrations + + private + + def harbor_registry_enabled! + render_404 unless Feature.enabled?(:harbor_registry_integration) + end + end + end +end diff --git a/app/controllers/projects/harbor/repositories_controller.rb b/app/controllers/projects/harbor/repositories_controller.rb new file mode 100644 index 00000000000..dd3e3dc1978 --- /dev/null +++ b/app/controllers/projects/harbor/repositories_controller.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Projects + module Harbor + class RepositoriesController < ::Projects::Harbor::ApplicationController + def show + render :index + end + end + end +end diff --git a/app/controllers/projects/incidents_controller.rb b/app/controllers/projects/incidents_controller.rb index 3395e75666e..293581a6744 100644 --- a/app/controllers/projects/incidents_controller.rb +++ b/app/controllers/projects/incidents_controller.rb @@ -6,6 +6,11 @@ class Projects::IncidentsController < Projects::ApplicationController before_action :authorize_read_issue! before_action :load_incident, only: [:show] + before_action do + push_frontend_feature_flag(:incident_escalations, @project) + push_frontend_feature_flag(:incident_timeline_event_tab, @project, default_enabled: :yaml) + push_licensed_feature(:incident_timeline_events) if @project.licensed_feature_available?(:incident_timeline_events) + end feature_category :incident_management @@ -43,3 +48,5 @@ class Projects::IncidentsController < Projects::ApplicationController IssueSerializer.new(current_user: current_user, project: incident.project) end end + +Projects::IncidentsController.prepend_mod diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 1b98810b09b..d4474b9d5a3 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -36,11 +36,6 @@ class Projects::IssuesController < Projects::ApplicationController before_action :authorize_import_issues!, only: [:import_csv] before_action :authorize_download_code!, only: [:related_branches] - # Limit the amount of issues created per minute - before_action -> { check_rate_limit!(:issues_create, scope: [@project, @current_user])}, - only: [:create], - if: -> { Feature.disabled?('rate_limited_service_issues_create', project, default_enabled: :yaml) } - before_action do push_frontend_feature_flag(:improved_emoji_picker, project, default_enabled: :yaml) push_frontend_feature_flag(:vue_issues_list, project&.group, default_enabled: :yaml) @@ -50,12 +45,10 @@ class Projects::IssuesController < Projects::ApplicationController end before_action only: :show do - push_frontend_feature_flag(:real_time_issue_sidebar, project, default_enabled: :yaml) push_frontend_feature_flag(:confidential_notes, project&.group, default_enabled: :yaml) push_frontend_feature_flag(:issue_assignees_widget, project, default_enabled: :yaml) push_frontend_feature_flag(:paginated_issue_discussions, project, default_enabled: :yaml) - push_frontend_feature_flag(:fix_comment_scroll, project, default_enabled: :yaml) - push_frontend_feature_flag(:work_items, project, default_enabled: :yaml) + push_frontend_feature_flag(:work_items, project&.group, default_enabled: :yaml) end around_action :allow_gitaly_ref_name_caching, only: [:discussions] @@ -79,13 +72,16 @@ class Projects::IssuesController < Projects::ApplicationController attr_accessor :vulnerability_id def index - set_issuables_index if !html_request? || Feature.disabled?(:vue_issues_list, project&.group, default_enabled: :yaml) - - @issues = @issuables + if html_request? && Feature.enabled?(:vue_issues_list, project&.group, default_enabled: :yaml) + set_sort_order + else + set_issuables_index + @issues = @issuables + end respond_to do |format| format.html - format.atom { render layout: 'xml.atom' } + format.atom { render layout: 'xml' } format.json do render json: { html: view_to_html_string("projects/issues/_issues"), @@ -112,6 +108,8 @@ class Projects::IssuesController < Projects::ApplicationController @issue = @noteable = service.execute + @add_related_issue = add_related_issue + @merge_request_to_resolve_discussions_of = service.merge_request_to_resolve_discussions_of if params[:discussion_to_resolve] @@ -128,6 +126,7 @@ class Projects::IssuesController < Projects::ApplicationController def create create_params = issue_params.merge( + add_related_issue: add_related_issue, merge_request_to_resolve_discussions_of: params[:merge_request_to_resolve_discussions_of], discussion_to_resolve: params[:discussion_to_resolve] ) @@ -150,7 +149,7 @@ class Projects::IssuesController < Projects::ApplicationController redirect_to project_issue_path(@project, @issue) else # NOTE: this CAPTCHA support method is indirectly included via IssuableActions - with_captcha_check_html_format { render :new } + with_captcha_check_html_format(spammable: spammable) { render :new } end end @@ -383,6 +382,11 @@ class Projects::IssuesController < Projects::ApplicationController action_name == 'service_desk' end + def add_related_issue + add_related_issue = project.issues.find_by_iid(params[:add_related_issue]) + add_related_issue if Ability.allowed?(current_user, :read_issue, add_related_issue) + end + # Overridden in EE def create_vulnerability_issue_feedback(issue); end end diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index bfc2fe6432d..b0f032a01e5 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -4,6 +4,8 @@ class Projects::JobsController < Projects::ApplicationController include SendFileUpload include ContinueParams + urgency :low, [:index, :show, :trace, :retry, :play, :cancel, :unschedule, :status, :erase, :raw] + before_action :find_job_as_build, except: [:index, :play, :show] before_action :find_job_as_processable, only: [:play, :show] before_action :authorize_read_build_trace!, only: [:trace, :raw] diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index 9bc9c19157a..0dcc2bc3181 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -111,9 +111,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic allow_tree_conflicts: display_merge_conflicts_in_diff? ) - if @merge_request.project.context_commits_enabled? - options[:context_commits] = @merge_request.recent_context_commits - end + options[:context_commits] = @merge_request.recent_context_commits render json: DiffsSerializer.new(request).represent(diffs, options) end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 6445f920db5..60d7920f83e 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -30,14 +30,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo before_action :set_issuables_index, only: [:index] before_action :authenticate_user!, only: [:assign_related_issues] before_action :check_user_can_push_to_source_branch!, only: [:rebase] - before_action only: [:index, :show] do - push_frontend_feature_flag(:mr_attention_requests, project, default_enabled: :yaml) - end before_action only: [:show] do push_frontend_feature_flag(:file_identifier_hash) push_frontend_feature_flag(:merge_request_widget_graphql, project, default_enabled: :yaml) - push_frontend_feature_flag(:default_merge_ref_for_diffs, project, default_enabled: :yaml) push_frontend_feature_flag(:core_security_mr_widget_counts, project) push_frontend_feature_flag(:paginated_notes, project, default_enabled: :yaml) push_frontend_feature_flag(:confidential_notes, project, default_enabled: :yaml) @@ -45,8 +41,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:restructured_mr_widget, project, default_enabled: :yaml) push_frontend_feature_flag(:refactor_mr_widgets_extensions, project, default_enabled: :yaml) push_frontend_feature_flag(:rebase_without_ci_ui, project, default_enabled: :yaml) - push_frontend_feature_flag(:rearrange_pipelines_table, project, default_enabled: :yaml) push_frontend_feature_flag(:markdown_continue_lists, project, default_enabled: :yaml) + push_frontend_feature_flag(:secure_vulnerability_training, project, default_enabled: :yaml) + push_frontend_feature_flag(:issue_assignees_widget, @project, default_enabled: :yaml) # Usage data feature flags push_frontend_feature_flag(:users_expanding_widgets_usage_data, project, default_enabled: :yaml) push_frontend_feature_flag(:diff_settings_usage_data, default_enabled: :yaml) @@ -87,7 +84,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo :ci_environments_status, :destroy, :rebase, - :discussions + :discussions, + :pipelines ] def index @@ -95,7 +93,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo respond_to do |format| format.html - format.atom { render layout: 'xml.atom' } + format.atom { render layout: 'xml' } format.json do render json: { html: view_to_html_string("projects/merge_requests/_merge_requests") @@ -220,8 +218,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo end def context_commits - return render_404 unless project.context_commits_enabled? - # Get commits from repository # or from cache if already merged commits = ContextCommitsFinder.new(project, @merge_request, { search: params[:search], limit: params[:limit], offset: params[:offset] }).execute @@ -553,12 +549,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo end def endpoint_metadata_url(project, merge_request) - params = request.query_parameters - params[:view] = "inline" - - if Feature.enabled?(:default_merge_ref_for_diffs, project, default_enabled: :yaml) - params = params.merge(diff_head: true) - end + params = request.query_parameters.merge(view: 'inline', diff_head: true) diffs_metadata_project_json_merge_request_path(project, merge_request, 'json', params) end diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb index ac94cc001dd..271c31b6429 100644 --- a/app/controllers/projects/pipeline_schedules_controller.rb +++ b/app/controllers/projects/pipeline_schedules_controller.rb @@ -10,6 +10,10 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController before_action :authorize_update_pipeline_schedule!, except: [:index, :new, :create, :play] before_action :authorize_admin_pipeline_schedule!, only: [:destroy] + before_action do + push_frontend_feature_flag(:pipeline_schedules_with_tags, @project, default_enabled: :yaml) + end + feature_category :continuous_integration # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/controllers/projects/pipelines/stages_controller.rb b/app/controllers/projects/pipelines/stages_controller.rb index ce08b49ce9f..0447bbf29e7 100644 --- a/app/controllers/projects/pipelines/stages_controller.rb +++ b/app/controllers/projects/pipelines/stages_controller.rb @@ -5,6 +5,10 @@ module Projects class StagesController < Projects::Pipelines::ApplicationController before_action :authorize_update_pipeline! + urgency :low, [ + :play_manual + ] + def play_manual ::Ci::PlayManualStageService .new(@project, current_user, pipeline: pipeline) diff --git a/app/controllers/projects/pipelines/tests_controller.rb b/app/controllers/projects/pipelines/tests_controller.rb index 25ec7ab1335..602fc02686a 100644 --- a/app/controllers/projects/pipelines/tests_controller.rb +++ b/app/controllers/projects/pipelines/tests_controller.rb @@ -42,9 +42,9 @@ module Projects end def test_suite - suite = builds.map do |build| + suite = builds.sum do |build| build.collect_test_reports!(Gitlab::Ci::Reports::TestReports.new) - end.sum + end Gitlab::Ci::Reports::TestFailureHistory.new(suite.failed.values, project).load! diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 7f680bbf121..8279bb20769 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -4,6 +4,9 @@ class Projects::PipelinesController < Projects::ApplicationController include ::Gitlab::Utils::StrongMemoize include RedisTracking + urgency :default, [:status] + urgency :low, [:index, :new, :builds, :show, :failures, :create, :stage, :retry, :dag, :cancel] + before_action :disable_query_limiting, only: [:create, :retry] before_action :pipeline, except: [:index, :new, :create, :charts, :config_variables] before_action :set_pipeline_path, only: [:show] @@ -13,13 +16,6 @@ class Projects::PipelinesController < Projects::ApplicationController before_action :authorize_create_pipeline!, only: [:new, :create, :config_variables] before_action :authorize_update_pipeline!, only: [:retry, :cancel] before_action :ensure_pipeline, only: [:show, :downloadable_artifacts] - before_action do - push_frontend_feature_flag(:rearrange_pipelines_table, project, default_enabled: :yaml) - end - - before_action do - push_frontend_feature_flag(:jobs_tab_vue, @project, default_enabled: :yaml) - end # Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596 before_action :redirect_for_legacy_scope_filter, only: [:index], if: -> { request.format.html? } @@ -55,8 +51,7 @@ class Projects::PipelinesController < Projects::ApplicationController respond_to do |format| format.html do - enable_code_quality_walkthrough_experiment - enable_ci_runner_templates_experiment + enable_runners_availability_section_experiment end format.json do Gitlab::PollingInterval.set_header(response, interval: POLLING_INTERVAL) @@ -166,14 +161,20 @@ class Projects::PipelinesController < Projects::ApplicationController end def retry - ::Ci::RetryPipelineWorker.perform_async(pipeline.id, current_user.id) # rubocop:disable CodeReuse/Worker + # Check for access before execution to allow for async execution while still returning access results + access_response = ::Ci::RetryPipelineService.new(@project, current_user).check_access(pipeline) + + if access_response.error? + response = { json: { errors: [access_response.message] }, status: access_response.http_status } + else + response = { json: {}, status: :no_content } + ::Ci::RetryPipelineWorker.perform_async(pipeline.id, current_user.id) # rubocop:disable CodeReuse/Worker + end respond_to do |format| - format.html do - redirect_back_or_default default: project_pipelines_path(project) + format.json do + render response end - - format.json { head :no_content } end end @@ -224,7 +225,7 @@ class Projects::PipelinesController < Projects::ApplicationController PipelineSerializer .new(project: @project, current_user: @current_user) .with_pagination(request, response) - .represent(@pipelines, disable_coverage: true, preload: true, code_quality_walkthrough: params[:code_quality_walkthrough].present?) + .represent(@pipelines, disable_coverage: true, preload: true) end def render_show @@ -309,28 +310,13 @@ class Projects::PipelinesController < Projects::ApplicationController params.permit(:scope, :username, :ref, :status, :source) end - def enable_code_quality_walkthrough_experiment - experiment(:code_quality_walkthrough, namespace: project.root_ancestor) do |e| - e.exclude! unless current_user - e.exclude! unless can?(current_user, :create_pipeline, project) - e.exclude! unless project.root_ancestor.recent? - e.exclude! if @pipelines_count.to_i > 0 - e.exclude! if helpers.has_gitlab_ci?(project) - - e.control {} - e.candidate {} - e.publish_to_database - end - end - - def enable_ci_runner_templates_experiment - experiment(:ci_runner_templates, namespace: project.root_ancestor) do |e| - e.exclude! unless current_user - e.exclude! unless can?(current_user, :create_pipeline, project) - e.exclude! if @pipelines_count.to_i > 0 - e.exclude! if helpers.has_gitlab_ci?(project) + def enable_runners_availability_section_experiment + return unless current_user + return unless can?(current_user, :create_pipeline, project) + return if @pipelines_count.to_i > 0 + return if helpers.has_gitlab_ci?(project) - e.control {} + experiment(:runners_availability_section, namespace: project.root_ancestor) do |e| e.candidate {} e.publish_to_database end diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index dc0614c6bdd..0279a65f262 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -13,8 +13,6 @@ class Projects::ProjectMembersController < Projects::ApplicationController def index @sort = params[:sort].presence || sort_value_name - @skip_groups = @project.related_group_ids - @group_links = @project.project_group_links @group_links = @group_links.search(params[:search_groups]) if params[:search_groups].present? @@ -24,25 +22,6 @@ class Projects::ProjectMembersController < Projects::ApplicationController end @project_members = present_members(non_invited_members.page(params[:page])) - - @project_member = @project.project_members.new - end - - def import - @projects = Project.visible_to_user_and_access_level(current_user, Gitlab::Access::MAINTAINER).order_id_desc - end - - def apply_import - source_project = Project.find(params[:source_project_id]) - - if can?(current_user, :admin_project_member, source_project) - status = @project.team.import(source_project, current_user) - notice = status ? "Successfully imported" : "Import failed" - else - return render_404 - end - - redirect_to(project_project_members_path(project), notice: notice) end # MembershipActions concern diff --git a/app/controllers/projects/redirect_controller.rb b/app/controllers/projects/redirect_controller.rb new file mode 100644 index 00000000000..6bcbe87ee42 --- /dev/null +++ b/app/controllers/projects/redirect_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +# Projects::RedirectController is used to resolve the route projects/:id. +# It's helpful for this to be in its own controller so that the +# ProjectsController can assume that :namespace_id exists +class Projects::RedirectController < ::ApplicationController + skip_before_action :authenticate_user! + + feature_category :projects + + def redirect_from_id + project = Project.find(params[:id]) + + if can?(current_user, :read_project, project) + redirect_to project + else + render_404 + end + end +end diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb index 7fba6cc5bf4..1a2baf96020 100644 --- a/app/controllers/projects/releases_controller.rb +++ b/app/controllers/projects/releases_controller.rb @@ -7,6 +7,7 @@ class Projects::ReleasesController < Projects::ApplicationController before_action :authorize_read_release! before_action :authorize_update_release!, only: %i[edit update] before_action :authorize_create_release!, only: :new + before_action :validate_suffix_path, :fetch_latest_tag, only: :latest_permalink before_action only: :index do push_frontend_feature_flag(:releases_index_apollo_client, project, default_enabled: :yaml) end @@ -26,10 +27,24 @@ class Projects::ReleasesController < Projects::ApplicationController redirect_to link.url end + def latest_permalink + unless @latest_tag.present? + return render_404 + end + + query_parameters_except_order_by = request.query_parameters.except(:order_by) + + redirect_url = project_release_url(@project, @latest_tag) + redirect_url += "/#{params[:suffix_path]}" if params[:suffix_path] + redirect_url += "?#{query_parameters_except_order_by.compact.to_param}" if query_parameters_except_order_by.present? + + redirect_to redirect_url + end + private - def releases - ReleasesFinder.new(@project, current_user).execute + def releases(params = {}) + ReleasesFinder.new(@project, current_user, params).execute end def authorize_update_release! @@ -51,4 +66,18 @@ class Projects::ReleasesController < Projects::ApplicationController def sanitized_tag_name CGI.unescape(params[:tag]) end + + # Default order_by is 'released_at', which is set in ReleasesFinder. + # Also if the passed order_by is invalid, we reject and default to 'released_at'. + def fetch_latest_tag + allowed_values = ['released_at'] + + params.reject! { |key, value| key.to_sym == :order_by && !allowed_values.any?(value) } + + @latest_tag = releases(order_by: params[:order_by]).first&.tag + end + + def validate_suffix_path + Gitlab::Utils.check_path_traversal!(params[:suffix_path]) if params[:suffix_path] + end end diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb index 39db7618db0..b77ce070492 100644 --- a/app/controllers/projects/runner_projects_controller.rb +++ b/app/controllers/projects/runner_projects_controller.rb @@ -14,7 +14,7 @@ class Projects::RunnerProjectsController < Projects::ApplicationController path = project_runners_path(project) - if @runner.assign_to(project, current_user) + if ::Ci::Runners::AssignRunnerService.new(@runner, @project, current_user).execute redirect_to path, notice: s_('Runners|Runner assigned to project.') else assign_to_messages = @runner.errors.messages[:assign_to] @@ -26,7 +26,8 @@ class Projects::RunnerProjectsController < Projects::ApplicationController def destroy runner_project = project.runner_projects.find(params[:id]) - runner_project.destroy + + ::Ci::Runners::UnassignRunnerService.new(runner_project, current_user).execute redirect_to project_runners_path(project), status: :found, notice: s_('Runners|Runner unassigned from project.') end diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index 192a29730d9..0eda8e3352d 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -14,7 +14,7 @@ class Projects::RunnersController < Projects::ApplicationController end def update - if Ci::UpdateRunnerService.new(@runner).update(runner_params) + if Ci::Runners::UpdateRunnerService.new(@runner).update(runner_params) redirect_to project_runner_path(@project, @runner), notice: _('Runner was successfully updated.') else render 'edit' @@ -23,14 +23,14 @@ class Projects::RunnersController < Projects::ApplicationController def destroy if @runner.only_for?(project) - Ci::UnregisterRunnerService.new(@runner).execute + Ci::Runners::UnregisterRunnerService.new(@runner, current_user).execute end redirect_to project_runners_path(@project), status: :found end def resume - if Ci::UpdateRunnerService.new(@runner).update(active: true) + if Ci::Runners::UpdateRunnerService.new(@runner).update(active: true) redirect_to project_runners_path(@project), notice: _('Runner was successfully updated.') else redirect_to project_runners_path(@project), alert: _('Runner was not updated.') @@ -38,7 +38,7 @@ class Projects::RunnersController < Projects::ApplicationController end def pause - if Ci::UpdateRunnerService.new(@runner).update(active: false) + if Ci::Runners::UpdateRunnerService.new(@runner).update(active: false) redirect_to project_runners_path(@project), notice: _('Runner was successfully updated.') else redirect_to project_runners_path(@project), alert: _('Runner was not updated.') diff --git a/app/controllers/projects/serverless/functions_controller.rb b/app/controllers/projects/serverless/functions_controller.rb index 3fc379a135a..b6f77a6d515 100644 --- a/app/controllers/projects/serverless/functions_controller.rb +++ b/app/controllers/projects/serverless/functions_controller.rb @@ -3,6 +3,7 @@ module Projects module Serverless class FunctionsController < Projects::ApplicationController + before_action :ensure_feature_enabled! before_action :authorize_read_cluster! feature_category :not_owned @@ -69,6 +70,10 @@ module Projects def serialize_function(function) Projects::Serverless::ServiceSerializer.new(current_user: @current_user, project: project).represent(function) end + + def ensure_feature_enabled! + render_404 unless Feature.enabled?(:deprecated_serverless, project, default_enabled: :yaml, type: :ops) + end end end end diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 1321111faaf..105f8efde7b 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -13,6 +13,10 @@ class Projects::ServicesController < Projects::ApplicationController before_action :set_deprecation_notice_for_prometheus_integration, only: [:edit, :update] before_action :redirect_deprecated_prometheus_integration, only: [:update] + before_action do + push_frontend_feature_flag(:integration_form_sections, project, default_enabled: :yaml) + end + respond_to :html layout "project_settings" diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index dd2fb57f7ac..3f4d26bb6ec 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -64,7 +64,7 @@ module Projects end def reset_registration_token - @project.reset_runners_token! + ::Ci::Runners::ResetRegistrationTokenService.new(@project, current_user).execute flash[:toast] = _("New runners registration token has been generated!") redirect_to namespace_project_settings_ci_cd_path diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb index 56e201c592f..43c72b358db 100644 --- a/app/controllers/projects/settings/operations_controller.rb +++ b/app/controllers/projects/settings/operations_controller.rb @@ -7,6 +7,10 @@ module Projects before_action :authorize_admin_operations! before_action :authorize_read_prometheus_alerts!, only: [:reset_alerting_token] + before_action do + push_frontend_feature_flag(:integrated_error_tracking, project) + end + respond_to :json, only: [:reset_alerting_token, :reset_pagerduty_token] helper_method :error_tracking_setting diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index 6472d3c3454..eb3579551bd 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -42,7 +42,7 @@ class Projects::TagsController < Projects::ApplicationController status = @tags_loading_error ? :service_unavailable : :ok format.html { render status: status } - format.atom { render layout: 'xml.atom', status: status } + format.atom { render layout: 'xml', status: status } end end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index 4f905a2d565..e447fc3f3fe 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -22,7 +22,6 @@ class Projects::TreeController < Projects::ApplicationController push_frontend_feature_flag(:refactor_blob_viewer, @project, default_enabled: :yaml) push_frontend_feature_flag(:highlight_js, @project, default_enabled: :yaml) push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) - push_frontend_feature_flag(:consolidated_edit_button, @project, default_enabled: :yaml) end feature_category :source_code_management diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 519d9cd0d52..507a8b66942 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -17,10 +17,10 @@ class ProjectsController < Projects::ApplicationController around_action :allow_gitaly_ref_name_caching, only: [:index, :show] before_action :disable_query_limiting, only: [:show, :create] - before_action :authenticate_user!, except: [:index, :show, :activity, :refs, :resolve, :unfoldered_environment_names] + before_action :authenticate_user!, except: [:index, :show, :activity, :refs, :unfoldered_environment_names] before_action :redirect_git_extension, only: [:show] - before_action :project, except: [:index, :new, :create, :resolve] - before_action :repository, except: [:index, :new, :create, :resolve] + before_action :project, except: [:index, :new, :create] + before_action :repository, except: [:index, :new, :create] before_action :verify_git_import_enabled, only: [:create] before_action :project_export_enabled, only: [:export, :download_export, :remove_export, :generate_new_export] before_action :present_project, only: [:edit] @@ -41,7 +41,6 @@ class ProjectsController < Projects::ApplicationController push_frontend_feature_flag(:increase_page_size_exponentially, @project, default_enabled: :yaml) push_frontend_feature_flag(:new_dir_modal, @project, default_enabled: :yaml) push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks) - push_frontend_feature_flag(:consolidated_edit_button, @project, default_enabled: :yaml) push_frontend_feature_flag(:work_items, @project, default_enabled: :yaml) end @@ -49,7 +48,7 @@ class ProjectsController < Projects::ApplicationController feature_category :projects, [ :index, :show, :new, :create, :edit, :update, :transfer, - :destroy, :resolve, :archive, :unarchive, :toggle_star, :activity + :destroy, :archive, :unarchive, :toggle_star, :activity ] feature_category :source_code_management, [:remove_fork, :housekeeping, :refs] @@ -174,7 +173,7 @@ class ProjectsController < Projects::ApplicationController format.atom do load_events @events = @events.select { |event| event.visible_to_user?(current_user) } - render layout: 'xml.atom' + render layout: 'xml' end end end @@ -325,16 +324,6 @@ class ProjectsController < Projects::ApplicationController end # rubocop: enable CodeReuse/ActiveRecord - def resolve - @project = Project.find(params[:id]) - - if can?(current_user, :read_project, @project) - redirect_to @project - else - render_404 - end - end - def unfoldered_environment_names respond_to do |format| format.json do @@ -346,11 +335,7 @@ class ProjectsController < Projects::ApplicationController private def refs_params - if Feature.enabled?(:strong_parameters_for_project_controller, @project, default_enabled: :yaml) - params.permit(:search, :sort, :ref, find: []) - else - params - end + params.permit(:search, :sort, :ref, find: []) end # Render project landing depending of which features are available diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 057c451ace2..7011bf856e3 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -15,7 +15,7 @@ class RegistrationsController < Devise::RegistrationsController before_action :load_recaptcha, only: :new before_action :set_invite_params, only: :new before_action only: [:create] do - check_rate_limit!(:user_sign_up, scope: request.ip) if Feature.enabled?(:rate_limit_user_sign_up_endpoint, default_enabled: :yaml) + check_rate_limit!(:user_sign_up, scope: request.ip) end before_action only: [:new] do diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index e38eeaed367..817da658f14 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -4,6 +4,7 @@ class SearchController < ApplicationController include ControllerWithCrossProjectAccessCheck include SearchHelper include RedisTracking + include SearchRateLimitable RESCUE_FROM_TIMEOUT_ACTIONS = [:count, :show, :autocomplete].freeze @@ -17,7 +18,7 @@ class SearchController < ApplicationController search_term_present = params[:search].present? || params[:term].present? search_term_present && !params[:project_id].present? end - before_action :check_email_search_rate_limit!, only: [:show, :count, :autocomplete] + before_action :check_search_rate_limit!, only: [:show, :count, :autocomplete] rescue_from ActiveRecord::QueryCanceled, with: :render_timeout @@ -25,6 +26,7 @@ class SearchController < ApplicationController feature_category :global_search urgency :high, [:opensearch] + urgency :low, [:count] def show @project = search_service.project @@ -201,12 +203,6 @@ class SearchController < ApplicationController render status: :request_timeout end end - - def check_email_search_rate_limit! - return unless search_service.params.email_lookup? - - check_rate_limit!(:user_email_lookup, scope: [current_user]) - end end SearchController.prepend_mod_with('SearchController') diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index d7eb3ccd274..4df0ef78907 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -40,30 +40,29 @@ class UploadsController < ApplicationController upload_model_class.find(params[:id]) end - def authorize_access! - authorized = - case model - when Note - can?(current_user, :read_project, model.project) - when Snippet, ProjectSnippet - can?(current_user, :read_snippet, model) - when User - # We validate the current user has enough (writing) - # access to itself when a secret is given. - # For instance, user avatars are readable by anyone, - # while temporary, user snippet uploads are not. - !secret? || can?(current_user, :update_user, model) - when Appearance - true - when Projects::Topic - true - else - permission = "read_#{model.class.underscore}".to_sym - - can?(current_user, permission, model) - end + def authorized? + case model + when Note + can?(current_user, :read_project, model.project) + when Snippet, ProjectSnippet + can?(current_user, :read_snippet, model) + when User + # We validate the current user has enough (writing) + # access to itself when a secret is given. + # For instance, user avatars are readable by anyone, + # while temporary, user snippet uploads are not. + !secret? || can?(current_user, :update_user, model) + when Appearance + true + when Projects::Topic + true + else + can?(current_user, "read_#{model.class.underscore}".to_sym, model) + end + end - render_unauthorized unless authorized + def authorize_access! + render_unauthorized unless authorized? end def authorize_create_access! diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index f6cef7e133c..dc02e4a3e87 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -24,7 +24,7 @@ class UsersController < ApplicationController before_action :authorize_read_user_profile!, only: [:calendar, :calendar_activities, :groups, :projects, :contributed, :starred, :snippets, :followers, :following] before_action only: [:exists] do - check_rate_limit!(:username_exists, scope: request.ip) if Feature.enabled?(:rate_limit_username_exists_endpoint, default_enabled: :yaml) + check_rate_limit!(:username_exists, scope: request.ip) end feature_category :users @@ -35,7 +35,7 @@ class UsersController < ApplicationController format.atom do load_events - render layout: 'xml.atom' + render layout: 'xml' end format.json do |