diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-19 09:08:42 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-08-19 09:08:42 +0000 |
commit | b76ae638462ab0f673e5915986070518dd3f9ad3 (patch) | |
tree | bdab0533383b52873be0ec0eb4d3c66598ff8b91 /app/controllers | |
parent | 434373eabe7b4be9593d18a585fb763f1e5f1a6f (diff) | |
download | gitlab-ce-b76ae638462ab0f673e5915986070518dd3f9ad3.tar.gz |
Add latest changes from gitlab-org/gitlab@14-2-stable-eev14.2.0-rc42
Diffstat (limited to 'app/controllers')
77 files changed, 697 insertions, 515 deletions
diff --git a/app/controllers/admin/clusters/applications_controller.rb b/app/controllers/admin/clusters/applications_controller.rb deleted file mode 100644 index 7400cc16175..00000000000 --- a/app/controllers/admin/clusters/applications_controller.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -class Admin::Clusters::ApplicationsController < Clusters::ApplicationsController - include EnforcesAdminAuthentication - - private - - def clusterable - @clusterable ||= InstanceClusterablePresenter.fabricate(Clusters::Instance.new, current_user: current_user) - end -end diff --git a/app/controllers/admin/integrations_controller.rb b/app/controllers/admin/integrations_controller.rb index 76c1c46e0e8..a3eb24b9b6f 100644 --- a/app/controllers/admin/integrations_controller.rb +++ b/app/controllers/admin/integrations_controller.rb @@ -2,19 +2,26 @@ class Admin::IntegrationsController < Admin::ApplicationController include IntegrationsActions - include IntegrationsHelper before_action :not_found, unless: -> { instance_level_integrations? } feature_category :integrations + def overrides + respond_to do |format| + format.json do + projects = Project.with_active_integration(integration.class).merge(::Integration.with_custom_settings) + serializer = ::Integrations::ProjectSerializer.new.with_pagination(request, response) + + render json: serializer.represent(projects) + end + format.html { render 'shared/integrations/overrides' } + end + end + private def find_or_initialize_non_project_specific_integration(name) Integration.find_or_initialize_non_project_specific_integration(name, instance: true) end - - def scoped_edit_integration_path(integration) - edit_admin_application_settings_integration_path(integration) - end end diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb index 7761ffaac84..3b408de5f01 100644 --- a/app/controllers/admin/runner_projects_controller.rb +++ b/app/controllers/admin/runner_projects_controller.rb @@ -3,7 +3,7 @@ class Admin::RunnerProjectsController < Admin::ApplicationController before_action :project, only: [:create] - feature_category :continuous_integration + feature_category :runner def create @runner = Ci::Runner.find(params[:runner_project][:runner_id]) diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb index d1c91d9617f..8c74352a179 100644 --- a/app/controllers/admin/runners_controller.rb +++ b/app/controllers/admin/runners_controller.rb @@ -4,19 +4,11 @@ class Admin::RunnersController < Admin::ApplicationController include RunnerSetupScripts before_action :runner, except: [:index, :tag_list, :runner_setup_scripts] - before_action only: [:index] do - push_frontend_feature_flag(:runner_list_view_vue_ui, current_user, default_enabled: :yaml) - end feature_category :runner - NUMBER_OF_RUNNERS_PER_PAGE = 30 - def index - finder = Ci::RunnersFinder.new(current_user: current_user, params: params) - @runners = finder.execute.page(params[:page]).per(NUMBER_OF_RUNNERS_PER_PAGE) @active_runners_count = Ci::Runner.online.count - @sort = finder.sort_key end def show diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb deleted file mode 100644 index d34773ee4dc..00000000000 --- a/app/controllers/admin/services_controller.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -class Admin::ServicesController < Admin::ApplicationController - include Integrations::Params - - before_action :integration, only: [:edit, :update] - before_action :disable_query_limiting, only: [:index] - - feature_category :integrations - - def index - @activated_services = Integration.for_template.active.sort_by(&:title) - @existing_instance_types = Integration.for_instance.pluck(:type) # rubocop: disable CodeReuse/ActiveRecord - end - - def edit - if integration.nil? || Integration.instance_exists_for?(integration.type) - redirect_to admin_application_settings_services_path, - alert: "Service is unknown or it doesn't exist" - end - end - - def update - if integration.update(integration_params[:integration]) - PropagateServiceTemplateWorker.perform_async(integration.id) if integration.active? # rubocop:disable CodeReuse/Worker - - redirect_to admin_application_settings_services_path, - notice: 'Application settings saved successfully' - else - render :edit - end - end - - private - - # rubocop: disable CodeReuse/ActiveRecord - def integration - @integration ||= Integration.find_by(id: params[:id], template: true) - @service ||= @integration # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/329759 - end - alias_method :service, :integration - # rubocop: enable CodeReuse/ActiveRecord - - def disable_query_limiting - Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/220357') - end -end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 145b4d10b16..3801906635f 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -26,9 +26,10 @@ class Admin::UsersController < Admin::ApplicationController def show end + # rubocop: disable CodeReuse/ActiveRecord def projects - @personal_projects = user.personal_projects - @joined_projects = user.projects.joined(@user) + @personal_projects = user.personal_projects.includes(:topics) + @joined_projects = user.projects.joined(@user).includes(:topics) end def keys @@ -136,7 +137,9 @@ class Admin::UsersController < Admin::ApplicationController end def unban - if update_user { |user| user.activate } + result = Users::UnbanService.new(current_user).execute(user) + + if result[:status] == :success redirect_back_or_admin_user(notice: _("Successfully unbanned")) else redirect_back_or_admin_user(alert: _("Error occurred. User was not unbanned")) @@ -145,7 +148,7 @@ class Admin::UsersController < Admin::ApplicationController def unlock if update_user { |user| user.unlock_access! } - redirect_back_or_admin_user(alert: _("Successfully unlocked")) + redirect_back_or_admin_user(notice: _("Successfully unlocked")) else redirect_back_or_admin_user(alert: _("Error occurred. User was not unlocked")) end diff --git a/app/controllers/clusters/applications_controller.rb b/app/controllers/clusters/applications_controller.rb deleted file mode 100644 index 91003e9580d..00000000000 --- a/app/controllers/clusters/applications_controller.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true - -class Clusters::ApplicationsController < Clusters::BaseController - before_action :cluster - before_action :authorize_create_cluster!, only: [:create] - before_action :authorize_update_cluster!, only: [:update] - before_action :authorize_admin_cluster!, only: [:destroy] - - def create - request_handler do - Clusters::Applications::CreateService - .new(@cluster, current_user, cluster_application_params) - .execute(request) - end - end - - def update - request_handler do - Clusters::Applications::UpdateService - .new(@cluster, current_user, cluster_application_params) - .execute(request) - end - end - - def destroy - request_handler do - Clusters::Applications::DestroyService - .new(@cluster, current_user, cluster_application_destroy_params) - .execute(request) - end - end - - private - - def request_handler - yield - - head :no_content - rescue Clusters::Applications::BaseService::InvalidApplicationError - render_404 - rescue StandardError - head :bad_request - end - - def cluster - @cluster ||= clusterable.clusters.find(params[:id]) || render_404 - end - - def cluster_application_params - params.permit(:application, :hostname, :pages_domain_id, :email, :stack, :host, :port, :protocol) - end - - def cluster_application_destroy_params - params.permit(:application) - end -end diff --git a/app/controllers/concerns/analytics/cycle_analytics/stage_actions.rb b/app/controllers/concerns/analytics/cycle_analytics/stage_actions.rb new file mode 100644 index 00000000000..eebc40f33f4 --- /dev/null +++ b/app/controllers/concerns/analytics/cycle_analytics/stage_actions.rb @@ -0,0 +1,96 @@ +# frozen_string_literal: true + +module Analytics + module CycleAnalytics + module StageActions + include Gitlab::Utils::StrongMemoize + extend ActiveSupport::Concern + + included do + include CycleAnalyticsParams + + before_action :validate_params, only: %i[median] + end + + def index + result = list_service.execute + + if result.success? + render json: cycle_analytics_configuration(result.payload[:stages]) + else + render json: { message: result.message }, status: result.http_status + end + end + + def median + render json: { value: data_collector.median.seconds } + end + + def average + render json: { value: data_collector.average.seconds } + end + + def records + serialized_records = data_collector.serialized_records do |relation| + add_pagination_headers(relation) + end + + render json: serialized_records + end + + def count + render json: { count: data_collector.count } + end + + private + + def parent + raise NotImplementedError + end + + def value_stream_class + raise NotImplementedError + end + + def add_pagination_headers(relation) + Gitlab::Pagination::OffsetHeaderBuilder.new( + request_context: self, + per_page: relation.limit_value, + page: relation.current_page, + next_page: relation.next_page, + prev_page: relation.prev_page, + params: permitted_cycle_analytics_params + ).execute(exclude_total_headers: true, data_without_counts: true) + end + + def stage + @stage ||= ::Analytics::CycleAnalytics::StageFinder.new(parent: parent, stage_id: params[:id]).execute + end + + def data_collector + @data_collector ||= Gitlab::Analytics::CycleAnalytics::DataCollector.new( + stage: stage, + params: request_params.to_data_collector_params + ) + end + + def value_stream + @value_stream ||= value_stream_class.build_default_value_stream(parent) + end + + def list_params + { value_stream: value_stream } + end + + def list_service + Analytics::CycleAnalytics::Stages::ListService.new(parent: parent, current_user: current_user, params: list_params) + end + + def cycle_analytics_configuration(stages) + stage_presenters = stages.map { |s| ::Analytics::CycleAnalytics::StagePresenter.new(s) } + + Analytics::CycleAnalytics::ConfigurationEntity.new(stages: stage_presenters) + end + end + end +end diff --git a/app/controllers/concerns/cycle_analytics_params.rb b/app/controllers/concerns/cycle_analytics_params.rb index b74e343f90b..626093b4588 100644 --- a/app/controllers/concerns/cycle_analytics_params.rb +++ b/app/controllers/concerns/cycle_analytics_params.rb @@ -16,9 +16,20 @@ module CycleAnalyticsParams end def options(params) - @options ||= { from: start_date(params), current_user: current_user }.merge(date_range(params)) + @options ||= {}.tap do |opts| + opts[:current_user] = current_user + opts[:projects] = params[:project_ids] if params[:project_ids] + opts[:group] = params[:group_id] if params[:group_id] + opts[:from] = params[:from] || start_date(params) + opts[:to] = params[:to] if params[:to] + opts[:end_event_filter] = params[:end_event_filter] if params[:end_event_filter] + opts.merge!(params.slice(*::Gitlab::Analytics::CycleAnalytics::RequestParams::FINDER_PARAM_NAMES)) + opts.merge!(date_range(params)) + end end + private + def start_date(params) case params[:start_date] when '7' @@ -41,6 +52,27 @@ module CycleAnalyticsParams date = field.is_a?(Date) || field.is_a?(Time) ? field : Date.parse(field) date.to_time.utc end + + def permitted_cycle_analytics_params + params.permit(*::Gitlab::Analytics::CycleAnalytics::RequestParams::STRONG_PARAMS_DEFINITION) + end + + def all_cycle_analytics_params + permitted_cycle_analytics_params.merge(current_user: current_user) + end + + def request_params + @request_params ||= ::Gitlab::Analytics::CycleAnalytics::RequestParams.new(all_cycle_analytics_params) + end + + def validate_params + if request_params.invalid? + render( + json: { message: 'Invalid parameters', errors: request_params.errors }, + status: :unprocessable_entity + ) + end + end end CycleAnalyticsParams.prepend_mod_with('CycleAnalyticsParams') diff --git a/app/controllers/concerns/dependency_proxy/auth.rb b/app/controllers/concerns/dependency_proxy/auth.rb deleted file mode 100644 index 1276feedba6..00000000000 --- a/app/controllers/concerns/dependency_proxy/auth.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -module DependencyProxy - module Auth - extend ActiveSupport::Concern - - included do - # We disable `authenticate_user!` since the `DependencyProxy::Auth` performs auth using JWT token - skip_before_action :authenticate_user!, raise: false - prepend_before_action :authenticate_user_from_jwt_token! - end - - def authenticate_user_from_jwt_token! - return unless dependency_proxy_for_private_groups? - - authenticate_with_http_token do |token, _| - user = user_from_token(token) - sign_in(user) if user - end - - request_bearer_token! unless current_user - end - - private - - def dependency_proxy_for_private_groups? - Feature.enabled?(:dependency_proxy_for_private_groups, default_enabled: true) - end - - def request_bearer_token! - # unfortunately, we cannot use https://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html#method-i-authentication_request - response.headers['WWW-Authenticate'] = ::DependencyProxy::Registry.authenticate_header - render plain: '', status: :unauthorized - end - - def user_from_token(token) - token_payload = DependencyProxy::AuthTokenService.decoded_token_payload(token) - User.find(token_payload['user_id']) - rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::ImmatureSignature - nil - end - end -end diff --git a/app/controllers/concerns/dependency_proxy/group_access.rb b/app/controllers/concerns/dependency_proxy/group_access.rb index 2a923d02752..07aca72b22f 100644 --- a/app/controllers/concerns/dependency_proxy/group_access.rb +++ b/app/controllers/concerns/dependency_proxy/group_access.rb @@ -12,15 +12,15 @@ module DependencyProxy private def verify_dependency_proxy_enabled! - render_404 unless group.dependency_proxy_feature_available? + render_404 unless group&.dependency_proxy_feature_available? end def authorize_read_dependency_proxy! - access_denied! unless can?(current_user, :read_dependency_proxy, group) + access_denied! unless can?(auth_user, :read_dependency_proxy, group) end def authorize_admin_dependency_proxy! - access_denied! unless can?(current_user, :admin_dependency_proxy, group) + access_denied! unless can?(auth_user, :admin_dependency_proxy, group) end end end diff --git a/app/controllers/concerns/find_snippet.rb b/app/controllers/concerns/find_snippet.rb index d51f1a1b3ad..8a4adbb608f 100644 --- a/app/controllers/concerns/find_snippet.rb +++ b/app/controllers/concerns/find_snippet.rb @@ -9,7 +9,7 @@ module FindSnippet # rubocop:disable CodeReuse/ActiveRecord def snippet strong_memoize(:snippet) do - snippet_klass.inc_relations_for_view.find_by(id: snippet_id) + snippet_klass.inc_relations_for_view.find_by(snippet_find_params) end end # rubocop:enable CodeReuse/ActiveRecord @@ -21,4 +21,8 @@ module FindSnippet def snippet_id params[:id] end + + def snippet_find_params + { id: snippet_id } + end end diff --git a/app/controllers/concerns/integrations_actions.rb b/app/controllers/concerns/integrations_actions.rb index f1fa5c845e2..dd066cc1b02 100644 --- a/app/controllers/concerns/integrations_actions.rb +++ b/app/controllers/concerns/integrations_actions.rb @@ -5,8 +5,9 @@ module IntegrationsActions included do include Integrations::Params + include IntegrationsHelper - before_action :integration, only: [:edit, :update, :test] + before_action :integration, only: [:edit, :update, :overrides, :test] end def edit diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb index 2664a7b7151..7ee680db7f9 100644 --- a/app/controllers/concerns/issuable_actions.rb +++ b/app/controllers/concerns/issuable_actions.rb @@ -4,6 +4,9 @@ module IssuableActions extend ActiveSupport::Concern include Gitlab::Utils::StrongMemoize include Gitlab::Cache::Helpers + include SpammableActions::AkismetMarkAsSpamAction + include SpammableActions::CaptchaCheck::HtmlFormatActionsSupport + include SpammableActions::CaptchaCheck::JsonFormatActionsSupport included do before_action :authorize_destroy_issuable!, only: :destroy @@ -25,17 +28,42 @@ module IssuableActions end def update - @issuable = update_service.execute(issuable) # rubocop:disable Gitlab/ModuleWithInstanceVariables - respond_to do |format| - format.html do - recaptcha_check_if_spammable { render :edit } + updated_issuable = update_service.execute(issuable) + # NOTE: We only assign the instance variable on this line, and use the local variable + # everywhere else in the method, to avoid having to add multiple `rubocop:disable` comments. + @issuable = updated_issuable # rubocop:disable Gitlab/ModuleWithInstanceVariables + + # NOTE: This check for `is_a?(Spammable)` is necessary because not all + # possible `issuable` types implement Spammable. Once they all implement Spammable, + # this check can be removed. + 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? + redirect_to spammable_path + else + with_captcha_check_html_format { render :edit } + end + end + + format.json do + with_captcha_check_json_format { render_entity_json } + end end - - format.json do - recaptcha_check_if_spammable(false) { render_entity_json } + else + respond_to do |format| + format.html do + render :edit + end + + format.json do + render_entity_json + end end end - rescue ActiveRecord::StaleObjectError render_conflict_response end @@ -171,12 +199,6 @@ module IssuableActions DiscussionSerializer.new(project: project, noteable: issuable, current_user: current_user, note_entity: ProjectNoteEntity) end - def recaptcha_check_if_spammable(should_redirect = true, &block) - return yield unless issuable.is_a? Spammable - - recaptcha_check_with_fallback(should_redirect, &block) - end - def render_conflict_response respond_to do |format| format.html do diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb index 55e0ed8cd42..97df3c7caea 100644 --- a/app/controllers/concerns/lfs_request.rb +++ b/app/controllers/concerns/lfs_request.rb @@ -4,6 +4,7 @@ # - a `#container` accessor # - a `#project` accessor # - a `#user` accessor +# - a `#deploy_token` accessor # - a `#authentication_result` accessor # - a `#can?(object, action, subject)` method # - a `#ci?` method @@ -83,26 +84,18 @@ module LfsRequest end def deploy_token_can_download_code? - deploy_token_present? && + deploy_token.present? && deploy_token.project == project && deploy_token.active? && deploy_token.read_repository? end - def deploy_token_present? - user && user.is_a?(DeployToken) - end - - def deploy_token - user - end - def lfs_upload_access? strong_memoize(:lfs_upload_access) do next false unless has_authentication_ability?(:push_code) next false if limit_exceeded? - lfs_deploy_token? || can?(user, :push_code, project) + lfs_deploy_token? || can?(user, :push_code, project) || can?(deploy_token, :push_code, project) end end @@ -111,7 +104,7 @@ module LfsRequest end def user_can_download_code? - has_authentication_ability?(:download_code) && can?(user, :download_code, project) && !deploy_token_present? + has_authentication_ability?(:download_code) && can?(user, :download_code, project) end def build_can_download_code? diff --git a/app/controllers/concerns/spammable_actions.rb b/app/controllers/concerns/spammable_actions.rb deleted file mode 100644 index eb1223f22a9..00000000000 --- a/app/controllers/concerns/spammable_actions.rb +++ /dev/null @@ -1,73 +0,0 @@ -# frozen_string_literal: true - -module SpammableActions - extend ActiveSupport::Concern - include Spam::Concerns::HasSpamActionResponseFields - - included do - before_action :authorize_submit_spammable!, only: :mark_as_spam - end - - def mark_as_spam - if Spam::MarkAsSpamService.new(target: spammable).execute - redirect_to spammable_path, notice: _("%{spammable_titlecase} was submitted to Akismet successfully.") % { spammable_titlecase: spammable.spammable_entity_type.titlecase } - else - redirect_to spammable_path, alert: _('Error with Akismet. Please check the logs for more info.') - end - end - - private - - def recaptcha_check_with_fallback(should_redirect = true, &fallback) - if should_redirect && spammable.valid? - redirect_to spammable_path - elsif spammable.render_recaptcha? - Gitlab::Recaptcha.load_configurations! - - respond_to do |format| - format.html do - # NOTE: format.html is still used by issue create, and uses the legacy HAML - # `_recaptcha_form.html.haml` rendered via the `projects/issues/verify` template. - render :verify - end - - format.json do - # format.json is used by all new Vue-based CAPTCHA implementations, which - # handle all of the CAPTCHA form rendering on the client via the Pajamas-based - # app/assets/javascripts/captcha/captcha_modal.vue - - # 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. - # See https://stackoverflow.com/q/26547466/25192 - render json: spam_action_response_fields(spammable), status: :conflict - end - end - else - yield - end - end - - # TODO: This method is currently only needed for issue create, to convert spam/CAPTCHA values from - # params, and instead be passed as headers, as the spam services now all expect. It can be removed - # when issue create is is converted to a client/JS based approach instead of the legacy HAML - # `_recaptcha_form.html.haml` which is rendered via the `projects/issues/verify` template. - # In that case, which is based on the legacy reCAPTCHA implementation using the HTML/HAML form, - # the 'g-recaptcha-response' field name comes from `Recaptcha::ClientHelper#recaptcha_tags` in the - # recaptcha gem, which is called from the HAML `_recaptcha_form.html.haml` form. - def extract_legacy_spam_params_to_headers - request.headers['X-GitLab-Captcha-Response'] = params['g-recaptcha-response'] || params[:captcha_response] - request.headers['X-GitLab-Spam-Log-Id'] = params[:spam_log_id] - end - - def spammable - raise NotImplementedError, "#{self.class} does not implement #{__method__}" - end - - def spammable_path - raise NotImplementedError, "#{self.class} does not implement #{__method__}" - end - - def authorize_submit_spammable! - access_denied! unless current_user.admin? - 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 new file mode 100644 index 00000000000..234c591ffb7 --- /dev/null +++ b/app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module SpammableActions::AkismetMarkAsSpamAction + extend ActiveSupport::Concern + include SpammableActions::Attributes + + included do + before_action :authorize_submit_spammable!, only: :mark_as_spam + end + + def mark_as_spam + if Spam::AkismetMarkAsSpamService.new(target: spammable).execute + redirect_to spammable_path, notice: _("%{spammable_titlecase} was submitted to Akismet successfully.") % { spammable_titlecase: spammable.spammable_entity_type.titlecase } + else + redirect_to spammable_path, alert: _('Error with Akismet. Please check the logs for more info.') + end + end + + private + + def authorize_submit_spammable! + access_denied! unless current_user.can_admin_all_resources? + end + + def spammable_path + raise NotImplementedError, "#{self.class} does not implement #{__method__}" + end +end diff --git a/app/controllers/concerns/spammable_actions/attributes.rb b/app/controllers/concerns/spammable_actions/attributes.rb new file mode 100644 index 00000000000..d7060e47c07 --- /dev/null +++ b/app/controllers/concerns/spammable_actions/attributes.rb @@ -0,0 +1,13 @@ +# 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 new file mode 100644 index 00000000000..7c047e02a1d --- /dev/null +++ b/app/controllers/concerns/spammable_actions/captcha_check/common.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module SpammableActions::CaptchaCheck + module Common + extend ActiveSupport::Concern + + 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? + + # 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 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 new file mode 100644 index 00000000000..f687c0fcf2d --- /dev/null +++ b/app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +# This module should *ONLY* be included if needed to support forms submits with HTML MIME type. +# In other words, forms handled by actions which use a `respond_to` of `format.html`. +# +# If the request is handled by actions via `format.json`, for example, for all Javascript based form +# submissions and Vue components which use Apollo and Axios, then the corresponding module +# which supports JSON format should be used instead. +module SpammableActions::CaptchaCheck::HtmlFormatActionsSupport + extend ActiveSupport::Concern + include SpammableActions::Attributes + include SpammableActions::CaptchaCheck::Common + + included do + before_action :convert_html_spam_params_to_headers, only: [:create, :update] + end + + private + + def with_captcha_check_html_format(&block) + captcha_render_lambda = -> { render :captcha_check } + with_captcha_check_common(captcha_render_lambda: captcha_render_lambda, &block) + end + + # Convert spam/CAPTCHA values from form field params to headers, because all spam-related services + # expect these values to be passed as headers. + # + # The 'g-recaptcha-response' field name comes from `Recaptcha::ClientHelper#recaptcha_tags` in the + # recaptcha gem. This is a field which is automatically included by calling the + # `#recaptcha_tags` method within a HAML template's form. + def convert_html_spam_params_to_headers + request.headers['X-GitLab-Captcha-Response'] = params['g-recaptcha-response'] if params['g-recaptcha-response'] + request.headers['X-GitLab-Spam-Log-Id'] = params[:spam_log_id] if params[:spam_log_id] + end +end 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 new file mode 100644 index 00000000000..0bfea05abc7 --- /dev/null +++ b/app/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +# This module should be included to support forms submits with a 'js' or 'json' type of MIME type. +# 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 +# +# 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) + # 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) + end +end diff --git a/app/controllers/customers_dot/proxy_controller.rb b/app/controllers/customers_dot/proxy_controller.rb deleted file mode 100644 index 5abf8a487c6..00000000000 --- a/app/controllers/customers_dot/proxy_controller.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -module CustomersDot - class ProxyController < ApplicationController - skip_before_action :authenticate_user! - skip_before_action :verify_authenticity_token - - feature_category :purchase - - BASE_URL = Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL - - def graphql - response = Gitlab::HTTP.post("#{BASE_URL}/graphql", - body: request.raw_post, - headers: { 'Content-Type' => 'application/json' } - ) - - render json: response.body, status: response.code - end - end -end diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index 01bb930a51b..d861ef646f8 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -81,7 +81,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController # rubocop: disable CodeReuse/ActiveRecord def preload_associations(projects) - projects.includes(:route, :creator, :group, namespace: [:route, :owner]).preload(:project_feature) + projects.includes(:route, :creator, :group, :topics, namespace: [:route, :owner]).preload(:project_feature) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index 5ef973e9bf3..e0973b0f3b4 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -87,7 +87,7 @@ class Explore::ProjectsController < Explore::ApplicationController # rubocop: disable CodeReuse/ActiveRecord def preload_associations(projects) - projects.includes(:route, :creator, :group, :project_feature, namespace: [:route, :owner]) + projects.includes(:route, :creator, :group, :project_feature, :topics, namespace: [:route, :owner]) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb index 69081835c4d..aa0d49902c3 100644 --- a/app/controllers/groups/application_controller.rb +++ b/app/controllers/groups/application_controller.rb @@ -13,16 +13,8 @@ class Groups::ApplicationController < ApplicationController before_action :set_sorting requires_cross_project_access - helper_method :can_manage_members? - private - def can_manage_members?(group = @group) - strong_memoize(:can_manage_members) do - can?(current_user, :admin_group_member, group) - end - end - def group @group ||= find_routable!(Group, params[:group_id] || params[:id], request.path_info) end diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb index 04b4d8ea9a7..96a3b38669d 100644 --- a/app/controllers/groups/boards_controller.rb +++ b/app/controllers/groups/boards_controller.rb @@ -7,7 +7,7 @@ class Groups::BoardsController < Groups::ApplicationController before_action :assign_endpoint_vars before_action do - push_frontend_feature_flag(:graphql_board_lists, group, default_enabled: false) + push_frontend_feature_flag(:graphql_board_lists, group, default_enabled: :yaml) 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(:swimlanes_buffered_rendering, group, default_enabled: :yaml) diff --git a/app/controllers/groups/clusters/applications_controller.rb b/app/controllers/groups/clusters/applications_controller.rb deleted file mode 100644 index ce6fda4143c..00000000000 --- a/app/controllers/groups/clusters/applications_controller.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -class Groups::Clusters::ApplicationsController < Clusters::ApplicationsController - include ControllerWithCrossProjectAccessCheck - - prepend_before_action :group - requires_cross_project_access - - private - - def clusterable - @clusterable ||= ClusterablePresenter.fabricate(group, current_user: current_user) - end - - def group - @group ||= find_routable!(Group, params[:group_id] || params[:id], request.path_info) - end -end diff --git a/app/controllers/groups/dependency_proxies_controller.rb b/app/controllers/groups/dependency_proxies_controller.rb index b896b240daf..b037aa52939 100644 --- a/app/controllers/groups/dependency_proxies_controller.rb +++ b/app/controllers/groups/dependency_proxies_controller.rb @@ -2,7 +2,7 @@ module Groups class DependencyProxiesController < Groups::ApplicationController - include DependencyProxy::GroupAccess + include ::DependencyProxy::GroupAccess before_action :authorize_admin_dependency_proxy!, only: :update before_action :dependency_proxy diff --git a/app/controllers/groups/dependency_proxy/application_controller.rb b/app/controllers/groups/dependency_proxy/application_controller.rb new file mode 100644 index 00000000000..fd9db41f748 --- /dev/null +++ b/app/controllers/groups/dependency_proxy/application_controller.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module Groups + module DependencyProxy + class ApplicationController < ::ApplicationController + EMPTY_AUTH_RESULT = Gitlab::Auth::Result.new(nil, nil, nil, nil).freeze + + delegate :actor, to: :@authentication_result, allow_nil: true + + # This allows auth_user to be set in the base ApplicationController + alias_method :authenticated_user, :actor + + # We disable `authenticate_user!` since the `DependencyProxy::ApplicationController` performs auth using JWT token + skip_before_action :authenticate_user!, raise: false + + prepend_before_action :authenticate_user_from_jwt_token! + + def authenticate_user_from_jwt_token! + return unless dependency_proxy_for_private_groups? + + authenticate_with_http_token do |token, _| + @authentication_result = EMPTY_AUTH_RESULT + + found_user = user_from_token(token) + sign_in(found_user) if found_user.is_a?(User) + end + + request_bearer_token! unless authenticated_user + end + + private + + def dependency_proxy_for_private_groups? + Feature.enabled?(:dependency_proxy_for_private_groups, default_enabled: true) + end + + def request_bearer_token! + # unfortunately, we cannot use https://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html#method-i-authentication_request + response.headers['WWW-Authenticate'] = ::DependencyProxy::Registry.authenticate_header + render plain: '', status: :unauthorized + end + + def user_from_token(token) + token_payload = ::DependencyProxy::AuthTokenService.decoded_token_payload(token) + + if token_payload['user_id'] + token_user = User.find(token_payload['user_id']) + return unless token_user + + @authentication_result = Gitlab::Auth::Result.new(token_user, nil, :user, []) + return token_user + elsif token_payload['deploy_token'] + deploy_token = DeployToken.active.find_by_token(token_payload['deploy_token']) + return unless deploy_token + + @authentication_result = Gitlab::Auth::Result.new(deploy_token, nil, :deploy_token, []) + return deploy_token + end + + nil + rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::ImmatureSignature + nil + end + end + end +end diff --git a/app/controllers/groups/dependency_proxy_auth_controller.rb b/app/controllers/groups/dependency_proxy_auth_controller.rb index e3e9bd88e24..60b2371fa9a 100644 --- a/app/controllers/groups/dependency_proxy_auth_controller.rb +++ b/app/controllers/groups/dependency_proxy_auth_controller.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -class Groups::DependencyProxyAuthController < ApplicationController - include DependencyProxy::Auth - +class Groups::DependencyProxyAuthController < ::Groups::DependencyProxy::ApplicationController feature_category :dependency_proxy def authenticate diff --git a/app/controllers/groups/dependency_proxy_for_containers_controller.rb b/app/controllers/groups/dependency_proxy_for_containers_controller.rb index e2c104f88a4..f7dc552bd3e 100644 --- a/app/controllers/groups/dependency_proxy_for_containers_controller.rb +++ b/app/controllers/groups/dependency_proxy_for_containers_controller.rb @@ -1,10 +1,12 @@ # frozen_string_literal: true -class Groups::DependencyProxyForContainersController < Groups::ApplicationController - include DependencyProxy::Auth +class Groups::DependencyProxyForContainersController < ::Groups::DependencyProxy::ApplicationController + include Gitlab::Utils::StrongMemoize include DependencyProxy::GroupAccess include SendFileUpload + include ::PackagesHelper # for event tracking + before_action :ensure_group before_action :ensure_token_granted! before_action :ensure_feature_enabled! @@ -22,6 +24,8 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro response.headers['Etag'] = "\"#{result[:manifest].digest}\"" content_type = result[:manifest].content_type + event_name = tracking_event_name(object_type: :manifest, from_cache: result[:from_cache]) + track_package_event(event_name, :dependency_proxy, namespace: group, user: auth_user) send_upload( result[:manifest].file, proxy: true, @@ -38,6 +42,8 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro .new(group, image, token, params[:sha]).execute if result[:status] == :success + event_name = tracking_event_name(object_type: :blob, from_cache: result[:from_cache]) + track_package_event(event_name, :dependency_proxy, namespace: group, user: auth_user) send_upload(result[:blob].file) else head result[:http_status] @@ -46,6 +52,12 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro private + def group + strong_memoize(:group) do + Group.find_by_full_path(params[:group_id], follow_redirects: request.get?) + end + end + def image params[:image] end @@ -54,11 +66,22 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro params[:tag] end + def tracking_event_name(object_type:, from_cache:) + event_name = "pull_#{object_type}" + event_name = "#{event_name}_from_cache" if from_cache + + event_name + end + def dependency_proxy @dependency_proxy ||= group.dependency_proxy_setting || group.create_dependency_proxy_setting end + def ensure_group + render_404 unless group + end + def ensure_feature_enabled! render_404 unless dependency_proxy.enabled end diff --git a/app/controllers/groups/email_campaigns_controller.rb b/app/controllers/groups/email_campaigns_controller.rb index d4c7b31c4b8..70c8a23d918 100644 --- a/app/controllers/groups/email_campaigns_controller.rb +++ b/app/controllers/groups/email_campaigns_controller.rb @@ -38,10 +38,12 @@ class Groups::EmailCampaignsController < Groups::ApplicationController create_track_url when :verify project_pipelines_url(group.projects.first) - when :trial + when :trial, :trial_short 'https://about.gitlab.com/free-trial/' - when :team + when :team, :team_short group_group_members_url(group) + when :admin_verify + project_settings_ci_cd_path(group.projects.first, ci_runner_templates: true, anchor: 'js-runners-settings') end end diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index d5e7653dea2..9b8d5cfe476 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -29,7 +29,7 @@ class Groups::GroupMembersController < Groups::ApplicationController .new(@group, current_user, params: filter_params) .execute(include_relations: requested_relations) - if can_manage_members? + if can?(current_user, :admin_group_member, @group) @skip_groups = @group.related_group_ids @invited_members = @members.invite diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb index 1cff658dd52..dbbfdd76fe8 100644 --- a/app/controllers/groups/runners_controller.rb +++ b/app/controllers/groups/runners_controller.rb @@ -1,14 +1,21 @@ # frozen_string_literal: true class Groups::RunnersController < Groups::ApplicationController - # Proper policies should be implemented per - # https://gitlab.com/gitlab-org/gitlab-foss/issues/45894 + # TODO Proper policies, such as `read_group_runners, should be implemented per + # https://gitlab.com/gitlab-org/gitlab/-/issues/334802 before_action :authorize_admin_group! - + before_action :runner_list_group_view_vue_ui_enabled, only: [:index] before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show] feature_category :runner + def index + end + + def runner_list_group_view_vue_ui_enabled + return render_404 unless Feature.enabled?(:runner_list_group_view_vue_ui, group, default_enabled: :yaml) + end + def show end diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb index 88c709e3f53..0f40c9bfd2c 100644 --- a/app/controllers/groups/settings/ci_cd_controller.rb +++ b/app/controllers/groups/settings/ci_cd_controller.rb @@ -60,6 +60,7 @@ module Groups def define_variables define_ci_variables + define_view_variables end def define_ci_variables @@ -69,6 +70,10 @@ module Groups .map { |variable| variable.present(current_user: current_user) } end + def define_view_variables + @content_class = 'limit-container-width' unless fluid_layout + end + def authorize_admin_group! return render_404 unless can?(current_user, :admin_group, group) end diff --git a/app/controllers/groups/settings/integrations_controller.rb b/app/controllers/groups/settings/integrations_controller.rb index 8e3b2cb5d1b..a7a1de03224 100644 --- a/app/controllers/groups/settings/integrations_controller.rb +++ b/app/controllers/groups/settings/integrations_controller.rb @@ -26,10 +26,6 @@ module Groups def find_or_initialize_non_project_specific_integration(name) Integration.find_or_initialize_non_project_specific_integration(name, group_id: group.id) end - - def scoped_edit_integration_path(integration) - edit_group_settings_integration_path(group, integration) - end end end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 66816d4c587..2796760fbe1 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -206,8 +206,6 @@ class GroupsController < Groups::ApplicationController protected def render_show_html - record_experiment_user(:invite_members_empty_group_version_a) if ::Gitlab.com? - render 'groups/show', locals: { trial: params[:trial] } end diff --git a/app/controllers/import/available_namespaces_controller.rb b/app/controllers/import/available_namespaces_controller.rb index c6211b33d28..0c2af13d3f3 100644 --- a/app/controllers/import/available_namespaces_controller.rb +++ b/app/controllers/import/available_namespaces_controller.rb @@ -4,6 +4,6 @@ class Import::AvailableNamespacesController < ApplicationController feature_category :importers def index - render json: NamespaceSerializer.new.represent(current_user.manageable_groups_with_routes) + render json: NamespaceSerializer.new.represent(current_user.manageable_groups_with_routes(include_groups_with_developer_maintainer_access: true)) end end diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb index 1121ecfb65c..53856e4575b 100644 --- a/app/controllers/import/base_controller.rb +++ b/app/controllers/import/base_controller.rb @@ -11,8 +11,7 @@ class Import::BaseController < ApplicationController format.json do render json: { imported_projects: serialized_imported_projects, provider_repos: serialized_provider_repos, - incompatible_repos: serialized_incompatible_repos, - namespaces: serialized_namespaces } + incompatible_repos: serialized_incompatible_repos } end format.html end @@ -74,14 +73,6 @@ class Import::BaseController < ApplicationController @already_added_projects ||= filtered(find_already_added_projects(provider_name)) end - def serialized_namespaces - NamespaceSerializer.new.represent(namespaces) - end - - def namespaces - current_user.manageable_groups_with_routes - end - # rubocop: disable CodeReuse/ActiveRecord def find_already_added_projects(import_type) current_user.created_projects.where(import_type: import_type).with_import_state diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index 3c81b698546..7f5750d2011 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -75,7 +75,10 @@ class InvitesController < ApplicationController end def track_invite_join_click - experiment('members/invite_email', actor: member).track(:join_clicked) if member && Members::InviteEmailExperiment.initial_invite_email?(params[:invite_type]) + return unless member && initial_invite_email? + + experiment(:invite_email_preview_text, actor: member).track(:join_clicked) if params[:experiment_name] == 'invite_email_preview_text' + Gitlab::Tracking.event(self.class.name, 'join_clicked', label: 'invite_email', property: member.id.to_s) end def authenticate_user! @@ -95,7 +98,12 @@ class InvitesController < ApplicationController def set_session_invite_params session[:invite_email] = member.invite_email - session[:originating_member_id] = member.id if Members::InviteEmailExperiment.initial_invite_email?(params[:invite_type]) + session[:originating_member_id] = member.id if initial_invite_email? + session[:invite_email_experiment_name] = params[:experiment_name] if initial_invite_email? && params[:experiment_name] + end + + def initial_invite_email? + params[:invite_type] == Emails::Members::INITIAL_INVITE end def sign_in_redirect_params diff --git a/app/controllers/jira_connect/app_descriptor_controller.rb b/app/controllers/jira_connect/app_descriptor_controller.rb index 0de42ad2452..a0f387631dd 100644 --- a/app/controllers/jira_connect/app_descriptor_controller.rb +++ b/app/controllers/jira_connect/app_descriptor_controller.rb @@ -44,27 +44,14 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController def modules modules = { - jiraDevelopmentTool: { - key: 'gitlab-development-tool', - application: { - value: 'GitLab' - }, - name: { - value: 'GitLab' - }, - url: HOME_URL, - logoUrl: logo_url, - capabilities: %w(branch commit pull_request) - }, postInstallPage: { key: 'gitlab-configuration', - name: { - value: 'GitLab Configuration' - }, + name: { value: 'GitLab Configuration' }, url: relative_to_base_path(jira_connect_subscriptions_path) } } + modules.merge!(development_tool_module) modules.merge!(build_information_module) modules.merge!(deployment_information_module) modules.merge!(feature_flag_module) @@ -76,6 +63,25 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController view_context.image_url('gitlab_logo.png') end + # See https://developer.atlassian.com/cloud/jira/software/modules/development-tool/ + def development_tool_module + { + jiraDevelopmentTool: { + actions: { + createBranch: { + templateUrl: new_jira_connect_branch_url + '?issue_key={issue.key}&issue_summary={issue.summary}' + } + }, + key: 'gitlab-development-tool', + application: { value: 'GitLab' }, + name: { value: 'GitLab' }, + url: HOME_URL, + logoUrl: logo_url, + capabilities: %w(branch commit pull_request) + } + } + end + # See: https://developer.atlassian.com/cloud/jira/software/modules/deployment/ def deployment_information_module { @@ -92,9 +98,7 @@ class JiraConnect::AppDescriptorController < JiraConnect::ApplicationController { jiraFeatureFlagInfoProvider: common_module_properties.merge( actions: {}, # TODO: create, link and list feature flags https://gitlab.com/gitlab-org/gitlab/-/issues/297386 - name: { - value: 'GitLab Feature Flags' - }, + name: { value: 'GitLab Feature Flags' }, key: 'gitlab-feature-flags' ) } diff --git a/app/controllers/jira_connect/branches_controller.rb b/app/controllers/jira_connect/branches_controller.rb new file mode 100644 index 00000000000..12ea6560e3a --- /dev/null +++ b/app/controllers/jira_connect/branches_controller.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +# NOTE: This controller does not inherit from JiraConnect::ApplicationController +# because we don't receive a JWT for this action, so we rely on standard GitLab authentication. +class JiraConnect::BranchesController < ApplicationController + feature_category :integrations + + def new + @new_branch_data = new_branch_data + end + + private + + def initial_branch_name + return unless params[:issue_key].present? + + Issue.to_branch_name( + params[:issue_key], + params[:issue_summary] + ) + end + + def new_branch_data + { + initial_branch_name: initial_branch_name, + success_state_svg_path: ActionController::Base.helpers.image_path('illustrations/merge_requests.svg') + } + end +end diff --git a/app/controllers/jira_connect/subscriptions_controller.rb b/app/controllers/jira_connect/subscriptions_controller.rb index 3ff12f29f10..a9c4dbf2b17 100644 --- a/app/controllers/jira_connect/subscriptions_controller.rb +++ b/app/controllers/jira_connect/subscriptions_controller.rb @@ -22,6 +22,13 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController def index @subscriptions = current_jira_installation.subscriptions.preload_namespace_route + + respond_to do |format| + format.html + format.json do + render json: JiraConnect::AppDataSerializer.new(@subscriptions, !!current_user).as_json + end + end end def create diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb index 85ee2204324..010b85e81bf 100644 --- a/app/controllers/jwt_controller.rb +++ b/app/controllers/jwt_controller.rb @@ -19,7 +19,7 @@ class JwtController < ApplicationController service = SERVICES[params[:service]] return head :not_found unless service - result = service.new(@authentication_result.project, @authentication_result.actor, auth_params) + result = service.new(@authentication_result.project, auth_user, auth_params) .execute(authentication_abilities: @authentication_result.authentication_abilities) render json: result, status: result[:http_status] @@ -67,7 +67,7 @@ class JwtController < ApplicationController end def additional_params - { scopes: scopes_param }.compact + { scopes: scopes_param, deploy_token: @authentication_result.deploy_token }.compact end # We have to parse scope here, because Docker Client does not send an array of scopes, @@ -83,8 +83,7 @@ class JwtController < ApplicationController def auth_user strong_memoize(:auth_user) do - actor = @authentication_result&.actor - actor.is_a?(User) ? actor : nil + @authentication_result.auth_user end end end diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index e2f8baa8226..effd3514c1b 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -57,6 +57,8 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController @codes = user.generate_otp_backup_codes! end + helpers.dismiss_account_recovery_regular_check + render 'create' else @error = _('Invalid pin code') @@ -105,6 +107,8 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController def codes Users::UpdateService.new(current_user, user: current_user).execute! do |user| @codes = user.generate_otp_backup_codes! + + helpers.dismiss_account_recovery_regular_check end end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 505608779ec..6cc602fec88 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -128,6 +128,7 @@ class ProfilesController < Profiles::ApplicationController :timezone, :job_title, :pronouns, + :pronunciation, status: [:emoji, :message, :availability] ) end diff --git a/app/controllers/projects/analytics/cycle_analytics/stages_controller.rb b/app/controllers/projects/analytics/cycle_analytics/stages_controller.rb index 7b4f6739a9b..2f9d70fede1 100644 --- a/app/controllers/projects/analytics/cycle_analytics/stages_controller.rb +++ b/app/controllers/projects/analytics/cycle_analytics/stages_controller.rb @@ -1,6 +1,9 @@ # frozen_string_literal: true class Projects::Analytics::CycleAnalytics::StagesController < Projects::ApplicationController + include ::Analytics::CycleAnalytics::StageActions + extend ::Gitlab::Utils::Override + respond_to :json feature_category :planning_analytics @@ -8,37 +11,19 @@ class Projects::Analytics::CycleAnalytics::StagesController < Projects::Applicat before_action :authorize_read_cycle_analytics! before_action :only_default_value_stream_is_allowed! - def index - result = list_service.execute - - if result.success? - render json: cycle_analytics_configuration(result.payload[:stages]) - else - render json: { message: result.message }, status: result.http_status - end - end - private - def only_default_value_stream_is_allowed! - render_404 if params[:value_stream_id] != Analytics::CycleAnalytics::Stages::BaseService::DEFAULT_VALUE_STREAM_NAME + override :parent + def parent + @project end - def value_stream - Analytics::CycleAnalytics::ProjectValueStream.build_default_value_stream(@project) + override :value_stream_class + def value_stream_class + Analytics::CycleAnalytics::ProjectValueStream end - def list_params - { value_stream: value_stream } - end - - def list_service - Analytics::CycleAnalytics::Stages::ListService.new(parent: @project, current_user: current_user, params: list_params) - end - - def cycle_analytics_configuration(stages) - stage_presenters = stages.map { |s| ::Analytics::CycleAnalytics::StagePresenter.new(s) } - - Analytics::CycleAnalytics::ConfigurationEntity.new(stages: stage_presenters) + def only_default_value_stream_is_allowed! + render_404 if params[:value_stream_id] != Analytics::CycleAnalytics::Stages::BaseService::DEFAULT_VALUE_STREAM_NAME end end diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 08066acb45c..acf6b6116b8 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -44,6 +44,7 @@ class Projects::BlobController < Projects::ApplicationController before_action do push_frontend_feature_flag(:refactor_blob_viewer, @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 def new diff --git a/app/controllers/projects/clusters/applications_controller.rb b/app/controllers/projects/clusters/applications_controller.rb deleted file mode 100644 index 6c5778124e8..00000000000 --- a/app/controllers/projects/clusters/applications_controller.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -class Projects::Clusters::ApplicationsController < Clusters::ApplicationsController - prepend_before_action :project - - private - - def clusterable - @clusterable ||= ClusterablePresenter.fabricate(project, current_user: current_user) - end - - def project - @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), request.path_info) - end -end diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index edf45e7063a..99f62c18593 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -20,10 +20,6 @@ class Projects::CompareController < Projects::ApplicationController # Validation before_action :validate_refs! - before_action do - push_frontend_feature_flag(:compare_repo_dropdown, source_project, default_enabled: :yaml) - end - feature_category :source_code_management # Diffs may be pretty chunky, the less is better in this endpoint. @@ -91,7 +87,6 @@ class Projects::CompareController < Projects::ApplicationController def target_project strong_memoize(:target_project) do next source_project unless params.key?(:from_project_id) - next source_project unless Feature.enabled?(:compare_repo_dropdown, source_project, default_enabled: :yaml) next source_project if params[:from_project_id].to_i == source_project.id target_project = target_projects(source_project).find_by_id(params[:from_project_id]) diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 8519841ee16..cac0aa9d513 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -87,17 +87,17 @@ class Projects::EnvironmentsController < Projects::ApplicationController @environment = project.environments.create(environment_params) if @environment.persisted? - redirect_to project_environment_path(project, @environment) + render json: { environment: @environment, path: project_environment_path(project, @environment) } else - render :new + render json: { message: @environment.errors.full_messages }, status: :bad_request end end def update if @environment.update(environment_params) - redirect_to project_environment_path(project, @environment) + render json: { environment: @environment, path: project_environment_path(project, @environment) } else - render :edit + render json: { message: @environment.errors.full_messages }, status: :bad_request end end diff --git a/app/controllers/projects/error_tracking_controller.rb b/app/controllers/projects/error_tracking_controller.rb index b4b03e219ab..8700d3c2198 100644 --- a/app/controllers/projects/error_tracking_controller.rb +++ b/app/controllers/projects/error_tracking_controller.rb @@ -27,7 +27,7 @@ class Projects::ErrorTrackingController < Projects::ErrorTracking::BaseControlle end def update - service = ErrorTracking::IssueUpdateService.new(project, current_user, issue_update_params) + service = ::ErrorTracking::IssueUpdateService.new(project, current_user, issue_update_params) result = service.execute return if render_errors(result) @@ -40,7 +40,7 @@ class Projects::ErrorTrackingController < Projects::ErrorTracking::BaseControlle private def render_index_json - service = ErrorTracking::ListIssuesService.new( + service = ::ErrorTracking::ListIssuesService.new( project, current_user, list_issues_params @@ -57,7 +57,7 @@ class Projects::ErrorTrackingController < Projects::ErrorTracking::BaseControlle end def render_issue_detail_json - service = ErrorTracking::IssueDetailsService.new(project, current_user, issue_details_params) + service = ::ErrorTracking::IssueDetailsService.new(project, current_user, issue_details_params) result = service.execute return if render_errors(result) @@ -91,13 +91,13 @@ class Projects::ErrorTrackingController < Projects::ErrorTracking::BaseControlle end def serialize_errors(errors) - ErrorTracking::ErrorSerializer + ::ErrorTracking::ErrorSerializer .new(project: project, user: current_user) .represent(errors) end def serialize_detailed_error(error) - ErrorTracking::DetailedErrorSerializer + ::ErrorTracking::DetailedErrorSerializer .new(project: project, user: current_user) .represent(error) end diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index 0f00fda4687..7135c0d959e 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -99,7 +99,7 @@ class Projects::ForksController < Projects::ApplicationController current_user: current_user ).execute - forks.includes(:route, :creator, :group, namespace: [:route, :owner]) + forks.includes(:route, :creator, :group, :topics, namespace: [:route, :owner]) end def fork_service diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 5d38e431c8a..bdfaaf2b143 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -7,7 +7,6 @@ class Projects::IssuesController < Projects::ApplicationController include ToggleAwardEmoji include IssuableCollections include IssuesCalendar - include SpammableActions include RecordUserLastActivity ISSUES_EXCEPT_ACTIONS = %i[index calendar new create bulk_update import_csv export_csv service_desk].freeze @@ -58,7 +57,7 @@ class Projects::IssuesController < Projects::ApplicationController push_frontend_feature_flag(:labels_widget, @project, default_enabled: :yaml) experiment(:invite_members_in_comment, namespace: @project.root_ancestor) do |experiment_instance| - experiment_instance.exclude! unless helpers.can_import_members? + experiment_instance.exclude! unless helpers.can_admin_project_member?(@project) experiment_instance.use {} experiment_instance.try(:invite_member_link) {} @@ -129,7 +128,6 @@ class Projects::IssuesController < Projects::ApplicationController end def create - extract_legacy_spam_params_to_headers create_params = issue_params.merge( merge_request_to_resolve_discussions_of: params[:merge_request_to_resolve_discussions_of], discussion_to_resolve: params[:discussion_to_resolve] @@ -149,10 +147,11 @@ class Projects::IssuesController < Projects::ApplicationController end end - respond_to do |format| - format.html do - recaptcha_check_with_fallback { render :new } - end + if @issue.valid? + 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 } end end diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index 49687a50ff6..778623a05c6 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -9,7 +9,7 @@ class Projects::JobsController < Projects::ApplicationController before_action :authorize_read_build_trace!, only: [:trace, :raw] before_action :authorize_read_build! before_action :authorize_update_build!, - except: [:index, :show, :status, :raw, :trace, :erase] + except: [:index, :show, :status, :raw, :trace, :erase, :cancel, :unschedule] before_action :authorize_erase_build!, only: [:erase] before_action :authorize_use_build_terminal!, only: [:terminal, :terminal_websocket_authorize] before_action :verify_api_request!, only: :terminal_websocket_authorize @@ -93,22 +93,28 @@ class Projects::JobsController < Projects::ApplicationController end def cancel - return respond_422 unless @build.cancelable? + service_response = Ci::BuildCancelService.new(@build, current_user).execute - @build.cancel - - if continue_params[:to] - redirect_to continue_params[:to] + if service_response.success? + destination = continue_params[:to].presence || builds_project_pipeline_path(@project, @build.pipeline.id) + redirect_to destination + elsif service_response.http_status == :forbidden + access_denied! else - redirect_to builds_project_pipeline_path(@project, @build.pipeline.id) + head service_response.http_status end end def unschedule - return respond_422 unless @build.scheduled? + service_response = Ci::BuildUnscheduleService.new(@build, current_user).execute - @build.unschedule! - redirect_to build_path(@build) + if service_response.success? + redirect_to build_path(@build) + elsif service_response.http_status == :forbidden + access_denied! + else + head service_response.http_status + end end def status diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index 9f1e2d8236a..ecc5ad1f84e 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -10,10 +10,6 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap before_action :apply_diff_view_cookie!, only: [:diffs, :diff_for_path] before_action :build_merge_request, except: [:create] - before_action do - push_frontend_feature_flag(:mr_collapsed_approval_rules, @project) - end - def new define_new_vars end diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index 88423bec915..8ccc658dfe7 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -27,10 +27,10 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic diff_options_hash[:paths] = params[:paths] if params[:paths] diffs = @compare.diffs_in_batch(params[:page], params[:per_page], diff_options: diff_options_hash) - positions = @merge_request.note_positions_for_paths(diffs.diff_file_paths, current_user) + unfoldable_positions = @merge_request.note_positions_for_paths(diffs.diff_file_paths, current_user).unfoldable environment = @merge_request.environments_for(current_user, latest: true).last - diffs.unfold_diff_files(positions.unfoldable) + diffs.unfold_diff_files(unfoldable_positions) diffs.write_cache options = { @@ -38,14 +38,29 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic merge_request: @merge_request, diff_view: diff_view, merge_ref_head_diff: render_merge_ref_head_diff?, - pagination_data: diffs.pagination_data + pagination_data: diffs.pagination_data, + allow_tree_conflicts: display_merge_conflicts_in_diff? } if diff_options_hash[:paths].blank? && Feature.enabled?(:diffs_batch_render_cached, project, default_enabled: :yaml) + # NOTE: Any variables that would affect the resulting json needs to be added to the cache_context to avoid stale cache issues. + cache_context = [ + current_user&.cache_key, + environment&.cache_key, + unfoldable_positions.map(&:to_h), + diff_view, + params[:w], + params[:expanded], + params[:page], + params[:per_page], + options[:merge_ref_head_diff], + options[:allow_tree_conflicts] + ] + render_cached( diffs, with: PaginatedDiffSerializer.new(current_user: current_user), - cache_context: -> (_) { [diff_view, params[:w], params[:expanded], params[:per_page], params[:page]] }, + cache_context: -> (_) { [Digest::SHA256.hexdigest(cache_context.to_s)] }, **options ) else @@ -56,8 +71,14 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic def diffs_metadata diffs = @compare.diffs(diff_options) + options = additional_attributes.merge( + only_context_commits: show_only_context_commits?, + merge_ref_head_diff: render_merge_ref_head_diff?, + allow_tree_conflicts: display_merge_conflicts_in_diff? + ) + render json: DiffsMetadataSerializer.new(project: @merge_request.project, current_user: current_user) - .represent(diffs, additional_attributes.merge(only_context_commits: show_only_context_commits?)) + .represent(diffs, options) end private @@ -82,7 +103,8 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic options = additional_attributes.merge( diff_view: "inline", - merge_ref_head_diff: render_merge_ref_head_diff? + merge_ref_head_diff: render_merge_ref_head_diff?, + allow_tree_conflicts: display_merge_conflicts_in_diff? ) if @merge_request.project.context_commits_enabled? @@ -213,4 +235,8 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter .track_mr_diffs_single_file_action(merge_request: @merge_request, user: current_user) end + + def display_merge_conflicts_in_diff? + Feature.enabled?(:display_merge_conflicts_in_diff, @merge_request.project) + end end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index cfa64bbc16d..8b3f2df69df 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -11,6 +11,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo include RecordUserLastActivity include SourcegraphDecorator include DiffHelper + include Gitlab::Cache::Helpers skip_before_action :merge_request, only: [:index, :bulk_update, :export_csv] before_action :apply_diff_view_cookie!, only: [:show] @@ -43,9 +44,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo # 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) + push_frontend_feature_flag(:diff_searching_usage_data, @project, default_enabled: :yaml) experiment(:invite_members_in_comment, namespace: @project.root_ancestor) do |experiment_instance| - experiment_instance.exclude! unless helpers.can_import_members? + experiment_instance.exclude! unless helpers.can_admin_project_member?(@project) experiment_instance.use {} experiment_instance.try(:invite_member_link) {} @@ -55,7 +57,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo end before_action do - push_frontend_feature_flag(:mr_collapsed_approval_rules, @project) push_frontend_feature_flag(:show_relevant_approval_rule_approvers, @project, default_enabled: :yaml) end @@ -92,6 +93,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo end end + # rubocop:disable Metrics/AbcSize def show close_merge_request_if_no_source_project @@ -128,7 +130,21 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo format.json do Gitlab::PollingInterval.set_header(response, interval: 10_000) - render json: serializer.represent(@merge_request, serializer: params[:serializer]) + if params[:serializer] == 'sidebar_extras' && Feature.enabled?(:merge_request_show_render_cached, @project, default_enabled: :yaml) + cache_context = [ + params[:serializer], + current_user&.cache_key, + @merge_request.assignees.map(&:cache_key), + @merge_request.reviewers.map(&:cache_key) + ] + + render_cached(@merge_request, + with: serializer, + cache_context: -> (_) { [Digest::SHA256.hexdigest(cache_context.to_s)] }, + serializer: params[:serializer]) + else + render json: serializer.represent(@merge_request, serializer: params[:serializer]) + end end format.patch do @@ -144,6 +160,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo end end end + # rubocop:enable Metrics/AbcSize def commits # Get context commits from repository diff --git a/app/controllers/projects/packages/infrastructure_registry_controller.rb b/app/controllers/projects/packages/infrastructure_registry_controller.rb index ee04cbb0062..4506a83634a 100644 --- a/app/controllers/projects/packages/infrastructure_registry_controller.rb +++ b/app/controllers/projects/packages/infrastructure_registry_controller.rb @@ -3,19 +3,14 @@ module Projects module Packages class InfrastructureRegistryController < Projects::ApplicationController - before_action :verify_feature_enabled! + include PackagesAccess + feature_category :infrastructure_as_code def show @package = project.packages.find(params[:id]) @package_files = @package.package_files.recent end - - private - - def verify_feature_enabled! - render_404 unless Feature.enabled?(:infrastructure_registry_page, default_enabled: :yaml) - end end end end diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index ba7c86434e0..a411264b350 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -12,12 +12,11 @@ class Projects::PipelinesController < Projects::ApplicationController before_action :authorize_read_ci_cd_analytics!, only: [:charts] 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(:pipeline_graph_layers_view, project, type: :development, default_enabled: :yaml) - push_frontend_feature_flag(:graphql_pipeline_details, project, type: :development, default_enabled: :yaml) - push_frontend_feature_flag(:graphql_pipeline_details_users, current_user, type: :development, default_enabled: :yaml) + push_frontend_feature_flag(:pipeline_source_filter, project, type: :development, default_enabled: :yaml) end - before_action :ensure_pipeline, only: [:show, :downloadable_artifacts] # 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? } @@ -32,10 +31,11 @@ class Projects::PipelinesController < Projects::ApplicationController feature_category :continuous_integration, [ :charts, :show, :config_variables, :stage, :cancel, :retry, - :builds, :dag, :failures, :status, :downloadable_artifacts, + :builds, :dag, :failures, :status, :index, :create, :new, :destroy ] feature_category :code_testing, [:test_report] + feature_category :build_artifacts, [:downloadable_artifacts] def index @pipelines = Ci::PipelinesFinder @@ -68,20 +68,22 @@ class Projects::PipelinesController < Projects::ApplicationController end def create - @pipeline = Ci::CreatePipelineService + service_response = Ci::CreatePipelineService .new(project, current_user, create_params) .execute(:web, ignore_skip_ci: true, save_on_errors: false) + @pipeline = service_response.payload + respond_to do |format| format.html do - if @pipeline.created_successfully? + if service_response.success? redirect_to project_pipeline_path(project, @pipeline) else render 'new', status: :bad_request end end format.json do - if @pipeline.created_successfully? + if service_response.success? render json: PipelineSerializer .new(project: project, current_user: current_user) .represent(@pipeline), diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index 370cd2b02a1..d0987492d2d 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -23,7 +23,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController .new(@project, current_user, params: filter_params) .execute(include_relations: requested_relations) - if helpers.can_manage_project_members?(@project) + if can?(current_user, :admin_project_member, @project) @invited_members = present_members(project_members.invite) @requesters = present_members(AccessRequestsFinder.new(@project).execute(current_user)) end diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index 3fff93abe5c..e86d2490282 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -19,7 +19,7 @@ class Projects::RawController < Projects::ApplicationController feature_category :source_code_management def show - @blob = @repository.blob_at(@ref, @path) + @blob = @repository.blob_at(@ref, @path, limit: Gitlab::Git::Blob::LFS_POINTER_MAX_SIZE) send_blob(@repository, @blob, inline: (params[:inline] != 'false'), allow_caching: Guest.can?(:download_code, @project)) end diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb index fa6adc9431d..5da81045e02 100644 --- a/app/controllers/projects/runner_projects_controller.rb +++ b/app/controllers/projects/runner_projects_controller.rb @@ -5,7 +5,7 @@ class Projects::RunnerProjectsController < Projects::ApplicationController layout 'project_settings' - feature_category :continuous_integration + feature_category :runner def create @runner = Ci::Runner.find(params[:runner_project][:runner_id]) diff --git a/app/controllers/projects/security/configuration_controller.rb b/app/controllers/projects/security/configuration_controller.rb index 3a473bb67e0..19de157357a 100644 --- a/app/controllers/projects/security/configuration_controller.rb +++ b/app/controllers/projects/security/configuration_controller.rb @@ -7,10 +7,6 @@ module Projects feature_category :static_application_security_testing - before_action only: [:show] do - push_frontend_feature_flag(:security_configuration_redesign, project, default_enabled: :yaml) - end - def show render_403 unless can?(current_user, :read_security_configuration, project) end diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index ef6d96e8737..0dcaab7160b 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -8,6 +8,7 @@ class Projects::ServicesController < Projects::ApplicationController before_action :authorize_admin_project! before_action :ensure_service_enabled before_action :integration + before_action :default_integration, only: [:edit, :update] before_action :web_hook_logs, only: [:edit, :update] before_action :set_deprecation_notice_for_prometheus_integration, only: [:edit, :update] before_action :redirect_deprecated_prometheus_integration, only: [:update] @@ -19,14 +20,22 @@ class Projects::ServicesController < Projects::ApplicationController feature_category :integrations def edit - @default_integration = Integration.default_integration(service.type, project) end def update - @integration.attributes = integration_params[:integration] - @integration.inherit_from_id = nil if integration_params[:integration][:inherit_from_id].blank? + attributes = integration_params[:integration] - saved = @integration.save(context: :manual_change) + if use_inherited_settings?(attributes) + @integration.inherit_from_id = default_integration.id + + if saved = @integration.save(context: :manual_change) + BulkUpdateIntegrationService.new(default_integration, [@integration]).execute + end + else + attributes[:inherit_from_id] = nil + @integration.attributes = attributes + saved = @integration.save(context: :manual_change) + end respond_to do |format| format.html do @@ -88,6 +97,10 @@ class Projects::ServicesController < Projects::ApplicationController end alias_method :service, :integration + def default_integration + @default_integration ||= Integration.default_integration(integration.type, project) + end + def web_hook_logs return unless integration.service_hook.present? @@ -115,4 +128,8 @@ class Projects::ServicesController < Projects::ApplicationController message = s_('PrometheusService|You can now manage your Prometheus settings on the %{operations_link_start}Operations%{operations_link_end} page. Fields on this page have been deprecated.') % { operations_link_start: operations_link_start, operations_link_end: "</a>" } flash.now[:alert] = message.html_safe end + + def use_inherited_settings?(attributes) + default_integration && attributes[:inherit_from_id] == default_integration.id.to_s + end end diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index de2ab16b5b1..97f9c5814e2 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true class Projects::SnippetsController < Projects::Snippets::ApplicationController + extend ::Gitlab::Utils::Override include SnippetsActions include ToggleAwardEmoji - include SpammableActions + include SpammableActions::AkismetMarkAsSpamAction before_action :check_snippets_available! @@ -45,4 +46,9 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController def spammable_path project_snippet_path(@project, @snippet) end + + override :snippet_find_params + def snippet_find_params + super.merge(project_id: project.id) + end end diff --git a/app/controllers/projects/templates_controller.rb b/app/controllers/projects/templates_controller.rb index df945a99c73..4bad6dc1b3d 100644 --- a/app/controllers/projects/templates_controller.rb +++ b/app/controllers/projects/templates_controller.rb @@ -5,7 +5,7 @@ class Projects::TemplatesController < Projects::ApplicationController before_action :authorize_can_read_issuable! before_action :get_template_class - feature_category :templates + feature_category :source_code_management def index templates = @template_type.template_subsets(project) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 0dbf7d40f87..bdb645e1934 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -31,10 +31,6 @@ class ProjectsController < Projects::ApplicationController # Project Export Rate Limit before_action :export_rate_limit, only: [:export, :download_export, :generate_new_export] - before_action only: [:edit] do - push_frontend_feature_flag(:allow_editing_commit_messages, @project) - end - before_action do push_frontend_feature_flag(:refactor_blob_viewer, @project, default_enabled: :yaml) push_frontend_feature_flag(:increase_page_size_exponentially, @project, default_enabled: :yaml) @@ -74,11 +70,6 @@ class ProjectsController < Projects::ApplicationController @project = ::Projects::CreateService.new(current_user, project_params(attributes: project_params_create_attributes)).execute if @project.saved? - experiment(:new_project_readme, actor: current_user).track( - :created, - property: active_new_project_tab, - value: project_params[:initialize_with_readme].to_i - ) redirect_to( project_path(@project, custom_import_params), notice: _("Project '%{project_name}' was successfully created.") % { project_name: @project.name } @@ -397,6 +388,7 @@ class ProjectsController < Projects::ApplicationController analytics_access_level operations_access_level security_and_compliance_access_level + container_registry_access_level ] end @@ -404,7 +396,6 @@ class ProjectsController < Projects::ApplicationController %i[ show_default_award_emojis squash_option - allow_editing_commit_messages mr_default_target_self ] end diff --git a/app/controllers/registrations/welcome_controller.rb b/app/controllers/registrations/welcome_controller.rb index 303ee431a4d..ced21b8f291 100644 --- a/app/controllers/registrations/welcome_controller.rb +++ b/app/controllers/registrations/welcome_controller.rb @@ -16,7 +16,7 @@ module Registrations result = ::Users::SignupService.new(current_user, update_params).execute if result[:status] == :success - return redirect_to new_users_sign_up_group_path if show_signup_onboarding? + return redirect_to new_users_sign_up_group_path(trial_params) if show_signup_onboarding? members = current_user.members @@ -41,7 +41,7 @@ module Registrations end def update_params - params.require(:user).permit(:role, :other_role, :setup_for_company, :email_opted_in) + params.require(:user).permit(:role, :other_role, :setup_for_company) end def requires_confirmation?(user) @@ -67,6 +67,10 @@ module Registrations def show_signup_onboarding? false end + + def trial_params + nil + end end end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 7b1060eba8f..cc985e84542 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -199,7 +199,9 @@ class RegistrationsController < Devise::RegistrationsController return unless member - experiment('members/invite_email', actor: member).track(:accepted) + experiment_name = session.delete(:invite_email_experiment_name) + experiment(:invite_email_preview_text, actor: member).track(:accepted) if experiment_name == 'invite_email_preview_text' + Gitlab::Tracking.event(self.class.name, 'accepted', label: 'invite_email', property: member.id.to_s) end def context_user diff --git a/app/controllers/repositories/git_http_client_controller.rb b/app/controllers/repositories/git_http_client_controller.rb index 76d9983d341..b3adda8c633 100644 --- a/app/controllers/repositories/git_http_client_controller.rb +++ b/app/controllers/repositories/git_http_client_controller.rb @@ -33,7 +33,7 @@ module Repositories end def authenticate_user - @authentication_result = Gitlab::Auth::Result.new + @authentication_result = Gitlab::Auth::Result::EMPTY if allow_basic_auth? && basic_auth_provided? login, password = user_name_and_password(request) diff --git a/app/controllers/repositories/lfs_api_controller.rb b/app/controllers/repositories/lfs_api_controller.rb index 4f2e02c78c3..a7719516cb6 100644 --- a/app/controllers/repositories/lfs_api_controller.rb +++ b/app/controllers/repositories/lfs_api_controller.rb @@ -10,6 +10,10 @@ module Repositories skip_before_action :lfs_check_access!, only: [:deprecated] before_action :lfs_check_batch_operation!, only: [:batch] + # added here as a part of the refactor, will be removed + # https://gitlab.com/gitlab-org/gitlab/-/issues/328692 + delegate :deploy_token, :user, to: :authentication_result, allow_nil: true + def batch unless objects.present? render_lfs_not_found @@ -141,7 +145,7 @@ module Repositories end def lfs_auth_header - return unless user.is_a?(User) + return unless user Gitlab::LfsToken.new(user).basic_encoding end diff --git a/app/controllers/repositories/lfs_locks_api_controller.rb b/app/controllers/repositories/lfs_locks_api_controller.rb index 19fc09ad4de..1d091a5bfcd 100644 --- a/app/controllers/repositories/lfs_locks_api_controller.rb +++ b/app/controllers/repositories/lfs_locks_api_controller.rb @@ -4,6 +4,10 @@ module Repositories class LfsLocksApiController < Repositories::GitHttpClientController include LfsRequest + # added here as a part of the refactor, will be removed + # https://gitlab.com/gitlab-org/gitlab/-/issues/328692 + delegate :deploy_token, :user, to: :authentication_result, allow_nil: true + def create @result = Lfs::LockFileService.new(project, user, lfs_params).execute diff --git a/app/controllers/repositories/lfs_storage_controller.rb b/app/controllers/repositories/lfs_storage_controller.rb index 36912948b77..6ec63a0f939 100644 --- a/app/controllers/repositories/lfs_storage_controller.rb +++ b/app/controllers/repositories/lfs_storage_controller.rb @@ -8,6 +8,10 @@ module Repositories skip_before_action :verify_workhorse_api!, only: :download + # added here as a part of the refactor, will be removed + # https://gitlab.com/gitlab-org/gitlab/-/issues/328692 + delegate :deploy_token, :user, to: :authentication_result, allow_nil: true + def download lfs_object = LfsObject.find_by_oid(oid) unless lfs_object && lfs_object.file.exists? diff --git a/app/controllers/runner_setup_controller.rb b/app/controllers/runner_setup_controller.rb index e0e9c5b7c23..89b635d5a6f 100644 --- a/app/controllers/runner_setup_controller.rb +++ b/app/controllers/runner_setup_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class RunnerSetupController < ApplicationController - feature_category :continuous_integration + feature_category :runner def platforms render json: Gitlab::Ci::RunnerInstructions::OS.merge(Gitlab::Ci::RunnerInstructions::OTHER_ENVIRONMENTS) diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 4160b528301..dbddb35d358 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -5,6 +5,8 @@ class SearchController < ApplicationController include SearchHelper include RedisTracking + RESCUE_FROM_TIMEOUT_ACTIONS = [:count, :show].freeze + track_redis_hll_event :show, name: 'i_search_total' around_action :allow_gitaly_ref_name_caching @@ -37,6 +39,7 @@ class SearchController < ApplicationController @search_service = Gitlab::View::Presenter::Factory.new(search_service, current_user: current_user).fabricate! @scope = @search_service.scope + @without_count = @search_service.without_count? @show_snippets = @search_service.show_snippets? @search_results = @search_service.search_results @search_objects = @search_service.search_objects @@ -154,12 +157,21 @@ class SearchController < ApplicationController end def render_timeout(exception) - raise exception unless action_name.to_sym == :show + raise exception unless action_name.to_sym.in?(RESCUE_FROM_TIMEOUT_ACTIONS) log_exception(exception) @timeout = true - render status: :request_timeout + + if count_action_name? + render json: {}, status: :request_timeout + else + render status: :request_timeout + end + end + + def count_action_name? + action_name.to_sym == :count end end diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 1c6168dbc2c..e81868faa6e 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -4,7 +4,7 @@ class SnippetsController < Snippets::ApplicationController include SnippetsActions include PreviewMarkdown include ToggleAwardEmoji - include SpammableActions + include SpammableActions::AkismetMarkAsSpamAction before_action :snippet, only: [:show, :edit, :raw, :toggle_award_emoji, :mark_as_spam] |