diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-19 23:18:09 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-19 23:18:09 +0000 |
commit | 6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde (patch) | |
tree | dc4d20fe6064752c0bd323187252c77e0a89144b /app/controllers | |
parent | 9868dae7fc0655bd7ce4a6887d4e6d487690eeed (diff) | |
download | gitlab-ce-6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde.tar.gz |
Add latest changes from gitlab-org/gitlab@15-4-stable-eev15.4.0-rc42
Diffstat (limited to 'app/controllers')
78 files changed, 882 insertions, 444 deletions
diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index 206a5b11e4b..0de2115d4d6 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -30,10 +30,7 @@ class AbuseReportsController < ApplicationController private def report_params - params.require(:abuse_report).permit(%i( - message - user_id - )) + params.require(:abuse_report).permit(:message, :user_id) end # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/controllers/acme_challenges_controller.rb b/app/controllers/acme_challenges_controller.rb index 67a39d8870b..4a7706db94e 100644 --- a/app/controllers/acme_challenges_controller.rb +++ b/app/controllers/acme_challenges_controller.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +# rubocop:disable Rails/ApplicationController class AcmeChallengesController < ActionController::Base def show if acme_order @@ -15,3 +16,4 @@ class AcmeChallengesController < ActionController::Base @acme_order ||= PagesDomainAcmeOrder.find_by_domain_and_token(params[:domain], params[:token]) end end +# rubocop:enable Rails/ApplicationController diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 6f21b123eb0..b75a7c4a2dd 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -18,23 +18,23 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController end feature_category :not_owned, [ # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned - :general, :reporting, :metrics_and_profiling, :network, - :preferences, :update, :reset_health_check_token - ] + :general, :reporting, :metrics_and_profiling, :network, + :preferences, :update, :reset_health_check_token + ] feature_category :metrics, [ - :create_self_monitoring_project, - :status_create_self_monitoring_project, - :delete_self_monitoring_project, - :status_delete_self_monitoring_project - ] + :create_self_monitoring_project, + :status_create_self_monitoring_project, + :delete_self_monitoring_project, + :status_delete_self_monitoring_project + ] urgency :low, [ - :create_self_monitoring_project, - :status_create_self_monitoring_project, - :delete_self_monitoring_project, - :status_delete_self_monitoring_project, - :reset_error_tracking_access_token - ] + :create_self_monitoring_project, + :status_create_self_monitoring_project, + :delete_self_monitoring_project, + :status_delete_self_monitoring_project, + :reset_error_tracking_access_token + ] feature_category :source_code_management, [:repository, :clear_repository_check_states] feature_category :continuous_integration, [:ci_cd, :reset_registration_token] diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb index b0d7c8cb8f2..d66b3cb4366 100644 --- a/app/controllers/admin/applications_controller.rb +++ b/app/controllers/admin/applications_controller.rb @@ -14,7 +14,7 @@ class Admin::ApplicationsController < Admin::ApplicationController end def show - @created = get_created_session + @created = get_created_session if Feature.disabled?('hash_oauth_secrets') end def new @@ -30,9 +30,14 @@ class Admin::ApplicationsController < Admin::ApplicationController if @application.persisted? flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) - set_created_session + if Feature.enabled?('hash_oauth_secrets') + @created = true + render :show + else + set_created_session - redirect_to admin_application_url(@application) + redirect_to admin_application_url(@application) + end else render :new end diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb index a53e832329f..251ba9e29f2 100644 --- a/app/controllers/admin/broadcast_messages_controller.rb +++ b/app/controllers/admin/broadcast_messages_controller.rb @@ -57,14 +57,15 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController end def broadcast_message_params - params.require(:broadcast_message).permit(%i( - theme - ends_at - message - starts_at - target_path - broadcast_type - dismissable - ), target_access_levels: []).reverse_merge!(target_access_levels: []) + params.require(:broadcast_message) + .permit(%i( + theme + ends_at + message + starts_at + target_path + broadcast_type + dismissable + ), target_access_levels: []).reverse_merge!(target_access_levels: []) end end diff --git a/app/controllers/admin/cohorts_controller.rb b/app/controllers/admin/cohorts_controller.rb index 468a1077694..ce3d769f35e 100644 --- a/app/controllers/admin/cohorts_controller.rb +++ b/app/controllers/admin/cohorts_controller.rb @@ -1,15 +1,20 @@ # frozen_string_literal: true class Admin::CohortsController < Admin::ApplicationController - include RedisTracking + include ProductAnalyticsTracking feature_category :devops_reports urgency :low + track_custom_event :index, + name: 'i_analytics_cohorts', + action: 'perform_analytics_usage_action', + label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', + destinations: %i[redis_hll snowplow] + def index @cohorts = load_cohorts - track_cohorts_visit end private @@ -22,7 +27,11 @@ class Admin::CohortsController < Admin::ApplicationController CohortsSerializer.new.represent(cohorts_results) end - def track_cohorts_visit - track_unique_redis_hll_event('i_analytics_cohorts') if trackable_html_request? + def tracking_namespace_source + nil + end + + def tracking_project_source + nil end end diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index 8fe106249c3..37dde065e70 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -14,14 +14,7 @@ class Admin::DashboardController < Admin::ApplicationController @groups = Group.order_id_desc.with_route.limit(10) @notices = Gitlab::ConfigChecker::PumaRuggedChecker.check @notices += Gitlab::ConfigChecker::ExternalDatabaseChecker.check - @redis_versions = [ - Gitlab::Redis::Queues, - Gitlab::Redis::SharedState, - Gitlab::Redis::Cache, - Gitlab::Redis::TraceChunks, - Gitlab::Redis::RateLimiting, - Gitlab::Redis::Sessions - ].map(&:version).uniq + @redis_versions = Gitlab::Redis::ALL_CLASSES.map(&:version).uniq end def stats diff --git a/app/controllers/admin/hook_logs_controller.rb b/app/controllers/admin/hook_logs_controller.rb index aa13673095d..a283d3abb0b 100644 --- a/app/controllers/admin/hook_logs_controller.rb +++ b/app/controllers/admin/hook_logs_controller.rb @@ -1,34 +1,17 @@ # frozen_string_literal: true -class Admin::HookLogsController < Admin::ApplicationController - include ::Integrations::HooksExecution +module Admin + class HookLogsController < Admin::ApplicationController + include WebHooks::HookLogActions - before_action :hook, only: [:show, :retry] - before_action :hook_log, only: [:show, :retry] + private - respond_to :html + def hook + @hook ||= SystemHook.find(params[:hook_id]) + end - feature_category :integrations - urgency :low, [:retry] - - def show - end - - def retry - result = hook.execute(hook_log.request_data, hook_log.trigger) - - set_hook_execution_notice(result) - - redirect_to edit_admin_hook_path(@hook) - end - - private - - def hook - @hook ||= SystemHook.find(params[:hook_id]) - end - - def hook_log - @hook_log ||= hook.web_hook_logs.find(params[:id]) + def after_retry_redirect_path + edit_admin_hook_path(hook) + end end end diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index 810801d4209..1dc6c68d8ca 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Admin::HooksController < Admin::ApplicationController - include ::Integrations::HooksExecution + include ::WebHooks::HookActions before_action :hook_logs, only: :edit @@ -27,7 +27,7 @@ class Admin::HooksController < Admin::ApplicationController end def hook_logs - @hook_logs ||= hook.web_hook_logs.recent.page(params[:page]) + @hook_logs ||= hook.web_hook_logs.recent.page(params[:page]).without_count end def hook_param_names diff --git a/app/controllers/admin/plan_limits_controller.rb b/app/controllers/admin/plan_limits_controller.rb index 7bfbabe8dfc..2cebc059830 100644 --- a/app/controllers/admin/plan_limits_controller.rb +++ b/app/controllers/admin/plan_limits_controller.rb @@ -28,24 +28,25 @@ class Admin::PlanLimitsController < Admin::ApplicationController end def plan_limits_params - params.require(:plan_limits).permit(%i[ - plan_id - conan_max_file_size - helm_max_file_size - maven_max_file_size - npm_max_file_size - nuget_max_file_size - pypi_max_file_size - terraform_module_max_file_size - generic_packages_max_file_size - ci_pipeline_size - ci_active_jobs - ci_active_pipelines - ci_project_subscriptions - ci_pipeline_schedules - ci_needs_size_limit - ci_registered_group_runners - ci_registered_project_runners - ]) + params.require(:plan_limits) + .permit(%i[ + plan_id + conan_max_file_size + helm_max_file_size + maven_max_file_size + npm_max_file_size + nuget_max_file_size + pypi_max_file_size + terraform_module_max_file_size + generic_packages_max_file_size + ci_pipeline_size + ci_active_jobs + ci_active_pipelines + ci_project_subscriptions + ci_pipeline_schedules + ci_needs_size_limit + ci_registered_group_runners + ci_registered_project_runners + ]) end end diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb index 24d7bd9ca7b..a0f72f5e58c 100644 --- a/app/controllers/admin/runners_controller.rb +++ b/app/controllers/admin/runners_controller.rb @@ -8,6 +8,10 @@ class Admin::RunnersController < Admin::ApplicationController push_frontend_feature_flag(:admin_runners_bulk_delete) end + before_action only: [:show] do + push_frontend_feature_flag(:enforce_runner_token_expires_at) + end + feature_category :runner urgency :low @@ -22,7 +26,7 @@ class Admin::RunnersController < Admin::ApplicationController end def update - if Ci::Runners::UpdateRunnerService.new(@runner).update(runner_params) + if Ci::Runners::UpdateRunnerService.new(@runner).execute(runner_params).success? respond_to do |format| format.html { redirect_to edit_admin_runner_path(@runner) } end @@ -39,7 +43,7 @@ class Admin::RunnersController < Admin::ApplicationController end def resume - if Ci::Runners::UpdateRunnerService.new(@runner).update(active: true) + if Ci::Runners::UpdateRunnerService.new(@runner).execute(active: true).success? redirect_to admin_runners_path, notice: _('Runner was successfully updated.') else redirect_to admin_runners_path, alert: _('Runner was not updated.') @@ -47,7 +51,7 @@ class Admin::RunnersController < Admin::ApplicationController end def pause - if Ci::Runners::UpdateRunnerService.new(@runner).update(active: false) + if Ci::Runners::UpdateRunnerService.new(@runner).execute(active: false).success? redirect_to admin_runners_path, notice: _('Runner was successfully updated.') else redirect_to admin_runners_path, alert: _('Runner was not updated.') diff --git a/app/controllers/admin/spam_logs_controller.rb b/app/controllers/admin/spam_logs_controller.rb index e4e866a8b60..3a55fc4b951 100644 --- a/app/controllers/admin/spam_logs_controller.rb +++ b/app/controllers/admin/spam_logs_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Admin::SpamLogsController < Admin::ApplicationController - feature_category :not_owned # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned + feature_category :instance_resiliency # rubocop: disable CodeReuse/ActiveRecord def index diff --git a/app/controllers/admin/topics_controller.rb b/app/controllers/admin/topics_controller.rb index 69bcfdf4791..e97ead12f71 100644 --- a/app/controllers/admin/topics_controller.rb +++ b/app/controllers/admin/topics_controller.rb @@ -49,16 +49,12 @@ class Admin::TopicsController < Admin::ApplicationController source_topic = Projects::Topic.find(merge_params[:source_topic_id]) target_topic = Projects::Topic.find(merge_params[:target_topic_id]) - begin - ::Topics::MergeService.new(source_topic, target_topic).execute - rescue ArgumentError => e - return render status: :bad_request, json: { type: :alert, message: e.message } - end + response = ::Topics::MergeService.new(source_topic, target_topic).execute + return render status: :bad_request, json: { type: :alert, message: response.message } if response.error? message = _('Topic %{source_topic} was successfully merged into topic %{target_topic}.') - redirect_to admin_topics_path, - status: :found, - notice: message % { source_topic: source_topic.name, target_topic: target_topic.name } + flash[:toast] = message % { source_topic: source_topic.name, target_topic: target_topic.name } + redirect_to admin_topics_path, status: :found end private diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 5cc0c8f3970..1a57d271271 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -105,7 +105,7 @@ class Admin::UsersController < Admin::ApplicationController return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user cannot be deactivated")) if user.blocked? return redirect_back_or_admin_user(notice: _("Successfully deactivated")) if user.deactivated? return redirect_back_or_admin_user(notice: _("Internal users cannot be deactivated")) if user.internal? - return redirect_back_or_admin_user(notice: _("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated") % { minimum_inactive_days: ::User::MINIMUM_INACTIVE_DAYS }) unless user.can_be_deactivated? + return redirect_back_or_admin_user(notice: _("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated") % { minimum_inactive_days: Gitlab::CurrentSettings.deactivate_dormant_users_period }) unless user.can_be_deactivated? user.deactivate redirect_back_or_admin_user(notice: _("Successfully deactivated")) diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb index 11377df7a10..5028544795c 100644 --- a/app/controllers/boards/issues_controller.rb +++ b/app/controllers/boards/issues_controller.rb @@ -77,10 +77,10 @@ module Boards :milestone, :assignees, project: [ - :route, - { - namespace: [:route] - } + :route, + { + namespace: [:route] + } ], labels: [:priorities], notes: [:award_emoji, :author] diff --git a/app/controllers/chaos_controller.rb b/app/controllers/chaos_controller.rb index 4e5af1945a4..6139168d29f 100644 --- a/app/controllers/chaos_controller.rb +++ b/app/controllers/chaos_controller.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +# rubocop:disable Rails/ApplicationController class ChaosController < ActionController::Base before_action :validate_chaos_secret, unless: :development_or_test? @@ -93,3 +94,4 @@ class ChaosController < ActionController::Base Rails.env.development? || Rails.env.test? end end +# rubocop:enable Rails/ApplicationController diff --git a/app/controllers/concerns/accepts_pending_invitations.rb b/app/controllers/concerns/accepts_pending_invitations.rb index 53dec698fa0..1723058c217 100644 --- a/app/controllers/concerns/accepts_pending_invitations.rb +++ b/app/controllers/concerns/accepts_pending_invitations.rb @@ -8,7 +8,6 @@ module AcceptsPendingInvitations if user.pending_invitations.load.any? user.accept_pending_invitations! - clear_stored_location_for(user: user) after_pending_invitations_hook end end @@ -16,10 +15,4 @@ module AcceptsPendingInvitations def after_pending_invitations_hook # no-op end - - def clear_stored_location_for(user:) - session_key = stored_location_key_for(user) - - session.delete(session_key) - end end diff --git a/app/controllers/concerns/dependency_proxy/group_access.rb b/app/controllers/concerns/dependency_proxy/group_access.rb index 45392625e45..e9fb2563e42 100644 --- a/app/controllers/concerns/dependency_proxy/group_access.rb +++ b/app/controllers/concerns/dependency_proxy/group_access.rb @@ -20,3 +20,5 @@ module DependencyProxy end end end + +DependencyProxy::GroupAccess.prepend_mod_with('DependencyProxy::GroupAccess') diff --git a/app/controllers/concerns/harbor/access.rb b/app/controllers/concerns/harbor/access.rb index 70de72f15fc..211566aeda7 100644 --- a/app/controllers/concerns/harbor/access.rb +++ b/app/controllers/concerns/harbor/access.rb @@ -17,7 +17,7 @@ module Harbor private def harbor_registry_enabled! - render_404 unless Feature.enabled?(:harbor_registry_integration) + render_404 unless Feature.enabled?(:harbor_registry_integration, defined?(group) ? group : project) end def authorize_read_harbor_registry! diff --git a/app/controllers/concerns/integrations/hooks_execution.rb b/app/controllers/concerns/integrations/hooks_execution.rb deleted file mode 100644 index fb26840168f..00000000000 --- a/app/controllers/concerns/integrations/hooks_execution.rb +++ /dev/null @@ -1,95 +0,0 @@ -# frozen_string_literal: true - -module Integrations::HooksExecution - extend ActiveSupport::Concern - - included do - attr_writer :hooks, :hook - end - - def index - self.hooks = relation.select(&:persisted?) - self.hook = relation.new - end - - def create - self.hook = relation.new(hook_params) - hook.save - - unless hook.valid? - self.hooks = relation.select(&:persisted?) - flash[:alert] = hook.errors.full_messages.join.html_safe - end - - redirect_to action: :index - end - - def update - if hook.update(hook_params) - flash[:notice] = _('Hook was successfully updated.') - redirect_to action: :index - else - render 'edit' - end - end - - def destroy - destroy_hook(hook) - - redirect_to action: :index, status: :found - end - - def edit - redirect_to(action: :index) unless hook - end - - private - - def hook_params - permitted = hook_param_names + trigger_values - permitted << { url_variables: [:key, :value] } - - ps = params.require(:hook).permit(*permitted).to_h - - ps[:url_variables] = ps[:url_variables].to_h { [_1[:key], _1[:value].presence] } if ps.key?(:url_variables) - - if action_name == 'update' && ps.key?(:url_variables) - supplied = ps[:url_variables] - ps[:url_variables] = hook.url_variables.merge(supplied).compact - end - - ps - end - - def hook_param_names - %i[enable_ssl_verification token url push_events_branch_filter] - end - - def destroy_hook(hook) - result = WebHooks::DestroyService.new(current_user).execute(hook) - - if result[:status] == :success - flash[:notice] = - if result[:async] - _("%{hook_type} was scheduled for deletion") % { hook_type: hook.model_name.human } - else - _("%{hook_type} was deleted") % { hook_type: hook.model_name.human } - end - else - flash[:alert] = result[:message] - end - end - - def set_hook_execution_notice(result) - http_status = result[:http_status] - message = result[:message] - - if http_status && http_status >= 200 && http_status < 400 - flash[:notice] = "Hook executed successfully: HTTP #{http_status}" - elsif http_status - flash[:alert] = "Hook executed successfully but returned HTTP #{http_status} #{message}" - else - flash[:alert] = "Hook execution failed: #{message}" - end - end -end diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb index f1d80e37674..7c3401a7e90 100644 --- a/app/controllers/concerns/issuable_actions.rb +++ b/app/controllers/concerns/issuable_actions.rb @@ -193,7 +193,10 @@ module IssuableActions end def render_cached_discussions(discussions, serializer, cache_context) - render_cached(discussions, with: serializer, cache_context: -> (_) { cache_context }, context: self) + render_cached(discussions, + with: serializer, + cache_context: -> (_) { cache_context }, + context: self) end def paginated_discussions diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb index fb11bece79c..8a67b62f28b 100644 --- a/app/controllers/concerns/membership_actions.rb +++ b/app/controllers/concerns/membership_actions.rb @@ -150,7 +150,11 @@ module MembershipActions when 'only' [:inherited] else - [:inherited, :direct] + if Feature.enabled?(:webui_members_inherited_users, current_user) + [:inherited, :direct, :shared_from_groups] + else + [:inherited, :direct] + end end end end diff --git a/app/controllers/concerns/packages_access.rb b/app/controllers/concerns/packages_access.rb index 6df2e064bb2..a7d16a5bc88 100644 --- a/app/controllers/concerns/packages_access.rb +++ b/app/controllers/concerns/packages_access.rb @@ -15,6 +15,6 @@ module PackagesAccess end def verify_read_package! - authorize_read_package!(project) + access_denied! unless can?(current_user, :read_package, project&.packages_policy_subject) end end diff --git a/app/controllers/concerns/product_analytics_tracking.rb b/app/controllers/concerns/product_analytics_tracking.rb index 260b433cc6f..8e936782e5a 100644 --- a/app/controllers/concerns/product_analytics_tracking.rb +++ b/app/controllers/concerns/product_analytics_tracking.rb @@ -66,7 +66,17 @@ module ProductAnalyticsTracking i_analytics_dev_ops_score: :route_hll_to_snowplow_phase2, p_analytics_merge_request: :route_hll_to_snowplow_phase2, i_analytics_instance_statistics: :route_hll_to_snowplow_phase2, - g_analytics_contribution: :route_hll_to_snowplow_phase2 + g_analytics_contribution: :route_hll_to_snowplow_phase2, + p_analytics_pipelines: :route_hll_to_snowplow_phase2, + p_analytics_code_reviews: :route_hll_to_snowplow_phase2, + p_analytics_valuestream: :route_hll_to_snowplow_phase2, + p_analytics_insights: :route_hll_to_snowplow_phase2, + p_analytics_issues: :route_hll_to_snowplow_phase2, + p_analytics_repo: :route_hll_to_snowplow_phase2, + g_analytics_insights: :route_hll_to_snowplow_phase2, + g_analytics_issues: :route_hll_to_snowplow_phase2, + g_analytics_productivity: :route_hll_to_snowplow_phase2, + i_analytics_cohorts: :route_hll_to_snowplow_phase2 } Feature.enabled?(events_to_ff[event.to_sym], tracking_namespace_source) diff --git a/app/controllers/concerns/verifies_with_email.rb b/app/controllers/concerns/verifies_with_email.rb index 1a3e7136481..782cae53c3f 100644 --- a/app/controllers/concerns/verifies_with_email.rb +++ b/app/controllers/concerns/verifies_with_email.rb @@ -7,11 +7,9 @@ module VerifiesWithEmail extend ActiveSupport::Concern include ActionView::Helpers::DateHelper - TOKEN_LENGTH = 6 - TOKEN_VALID_FOR_MINUTES = 60 - included do prepend_before_action :verify_with_email, only: :create, unless: -> { two_factor_enabled? } + skip_before_action :required_signup_info, only: :successful_verification end def verify_with_email @@ -76,7 +74,8 @@ module VerifiesWithEmail def send_verification_instructions(user) return if send_rate_limited?(user) - raw_token, encrypted_token = generate_token + service = Users::EmailVerification::GenerateTokenService.new(attr: :unlock_token) + raw_token, encrypted_token = service.execute user.unlock_token = encrypted_token user.lock_access!({ send_instructions: false }) send_verification_instructions_email(user, raw_token) @@ -88,27 +87,20 @@ module VerifiesWithEmail Notify.verification_instructions_email( user.id, token: token, - expires_in: TOKEN_VALID_FOR_MINUTES).deliver_later + expires_in: Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES).deliver_later log_verification(user, :instructions_sent) end def verify_token(user, token) - return handle_verification_failure(user, :rate_limited) if verification_rate_limited?(user) - return handle_verification_failure(user, :invalid) unless valid_token?(user, token) - return handle_verification_failure(user, :expired) if expired_token?(user) - - handle_verification_success(user) - end - - def generate_token - raw_token = SecureRandom.random_number(10**TOKEN_LENGTH).to_s.rjust(TOKEN_LENGTH, '0') - encrypted_token = digest_token(raw_token) - [raw_token, encrypted_token] - end + service = Users::EmailVerification::ValidateTokenService.new(attr: :unlock_token, user: user, token: token) + result = service.execute - def digest_token(token) - Devise.token_generator.digest(User, :unlock_token, token) + if result[:status] == :success + handle_verification_success(user) + else + handle_verification_failure(user, result[:reason], result[:message]) + end end def render_sign_in_rate_limited @@ -122,44 +114,17 @@ module VerifiesWithEmail distance_of_time_in_words(interval_in_seconds) end - def verification_rate_limited?(user) - Gitlab::ApplicationRateLimiter.throttled?(:email_verification, scope: user.unlock_token) - end - def send_rate_limited?(user) Gitlab::ApplicationRateLimiter.throttled?(:email_verification_code_send, scope: user) end - def expired_token?(user) - user.locked_at < (Time.current - TOKEN_VALID_FOR_MINUTES.minutes) - end - - def valid_token?(user, token) - user.unlock_token == digest_token(token) - end - - def handle_verification_failure(user, reason) - message = case reason - when :rate_limited - s_("IdentityVerification|You've reached the maximum amount of tries. "\ - 'Wait %{interval} or resend a new code and try again.') % { interval: email_verification_interval } - when :expired - s_('IdentityVerification|The code has expired. Resend a new code and try again.') - when :invalid - s_('IdentityVerification|The code is incorrect. Enter it again, or resend a new code.') - end - + def handle_verification_failure(user, reason, message) user.errors.add(:base, message) log_verification(user, :failed_attempt, reason) prompt_for_email_verification(user) end - def email_verification_interval - interval_in_seconds = Gitlab::ApplicationRateLimiter.rate_limits[:email_verification][:interval] - distance_of_time_in_words(interval_in_seconds) - end - def handle_verification_success(user) user.unlock_access! log_verification(user, :successful) diff --git a/app/controllers/concerns/web_hooks/hook_actions.rb b/app/controllers/concerns/web_hooks/hook_actions.rb new file mode 100644 index 00000000000..ea11f13c7ef --- /dev/null +++ b/app/controllers/concerns/web_hooks/hook_actions.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +module WebHooks + module HookActions + extend ActiveSupport::Concern + include HookExecutionNotice + + included do + attr_writer :hooks, :hook + end + + def index + self.hooks = relation.select(&:persisted?) + self.hook = relation.new + end + + def create + self.hook = relation.new(hook_params) + hook.save + + unless hook.valid? + self.hooks = relation.select(&:persisted?) + flash[:alert] = hook.errors.full_messages.join.html_safe + end + + redirect_to action: :index + end + + def update + if hook.update(hook_params) + flash[:notice] = _('Hook was successfully updated.') + redirect_to action: :index + else + render 'edit' + end + end + + def destroy + destroy_hook(hook) + + redirect_to action: :index, status: :found + end + + def edit + redirect_to(action: :index) unless hook + end + + private + + def hook_params + permitted = hook_param_names + trigger_values + permitted << { url_variables: [:key, :value] } + + ps = params.require(:hook).permit(*permitted).to_h + + ps[:url_variables] = ps[:url_variables].to_h { [_1[:key], _1[:value].presence] } if ps.key?(:url_variables) + + if action_name == 'update' && ps.key?(:url_variables) + supplied = ps[:url_variables] + ps[:url_variables] = hook.url_variables.merge(supplied).compact + end + + ps + end + + def hook_param_names + %i[enable_ssl_verification token url push_events_branch_filter] + end + + def destroy_hook(hook) + result = WebHooks::DestroyService.new(current_user).execute(hook) + + if result[:status] == :success + flash[:notice] = + if result[:async] + format(_("%{hook_type} was scheduled for deletion"), hook_type: hook.model_name.human) + else + format(_("%{hook_type} was deleted"), hook_type: hook.model_name.human) + end + else + flash[:alert] = result[:message] + end + end + end +end diff --git a/app/controllers/concerns/web_hooks/hook_execution_notice.rb b/app/controllers/concerns/web_hooks/hook_execution_notice.rb new file mode 100644 index 00000000000..d651313b30d --- /dev/null +++ b/app/controllers/concerns/web_hooks/hook_execution_notice.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module WebHooks + module HookExecutionNotice + private + + def set_hook_execution_notice(result) + http_status = result[:http_status] + message = result[:message] + + if http_status && http_status >= 200 && http_status < 400 + flash[:notice] = "Hook executed successfully: HTTP #{http_status}" + elsif http_status + flash[:alert] = "Hook executed successfully but returned HTTP #{http_status} #{message}" + else + flash[:alert] = "Hook execution failed: #{message}" + end + end + end +end diff --git a/app/controllers/concerns/web_hooks/hook_log_actions.rb b/app/controllers/concerns/web_hooks/hook_log_actions.rb new file mode 100644 index 00000000000..f3378d7c857 --- /dev/null +++ b/app/controllers/concerns/web_hooks/hook_log_actions.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module WebHooks + module HookLogActions + extend ActiveSupport::Concern + include HookExecutionNotice + + included do + before_action :hook, only: [:show, :retry] + before_action :hook_log, only: [:show, :retry] + + respond_to :html + + feature_category :integrations + urgency :low, [:retry] + end + + def show + hide_search_settings + end + + def retry + execute_hook + redirect_to after_retry_redirect_path + end + + private + + # rubocop:disable Gitlab/ModuleWithInstanceVariables + def hook_log + @hook_log ||= hook.web_hook_logs.find(params[:id]) + end + # rubocop:enable Gitlab/ModuleWithInstanceVariables + + def execute_hook + result = hook.execute(hook_log.request_data, hook_log.trigger) + set_hook_execution_notice(result) + end + + def hide_search_settings + @hide_search_settings ||= true + end + end +end diff --git a/app/controllers/groups/observability_controller.rb b/app/controllers/groups/observability_controller.rb new file mode 100644 index 00000000000..5b6503494c4 --- /dev/null +++ b/app/controllers/groups/observability_controller.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true +module Groups + class ObservabilityController < Groups::ApplicationController + feature_category :tracing + + content_security_policy do |p| + next if p.directives.blank? + + default_frame_src = p.directives['frame-src'] || p.directives['default-src'] + + # When ObservabilityUI is not authenticated, it needs to be able to redirect to the GL sign-in page, hence 'self' + frame_src_values = Array.wrap(default_frame_src) | [ObservabilityController.observability_url, "'self'"] + + p.frame_src(*frame_src_values) + end + + before_action :check_observability_allowed, only: :index + + def index + # Format: https://observe.gitlab.com/-/GROUP_ID + @observability_iframe_src = "#{ObservabilityController.observability_url}/-/#{@group.id}" + + # Uncomment below for testing with local GDK + # @observability_iframe_src = "#{ObservabilityController.observability_url}/9970?groupId=14485840" + + render layout: 'group', locals: { base_layout: 'layouts/fullscreen' } + end + + private + + def self.observability_url + return ENV['OVERRIDE_OBSERVABILITY_URL'] if ENV['OVERRIDE_OBSERVABILITY_URL'] + # TODO Make observability URL configurable https://gitlab.com/gitlab-org/opstrace/opstrace-ui/-/issues/80 + return "https://staging.observe.gitlab.com" if Gitlab.staging? + + "https://observe.gitlab.com" + end + + def check_observability_allowed + return render_404 unless self.class.observability_url.present? + + render_404 unless can?(current_user, :read_observability, @group) + end + end +end diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb index aeb54527c69..652f12e34ba 100644 --- a/app/controllers/groups/runners_controller.rb +++ b/app/controllers/groups/runners_controller.rb @@ -5,12 +5,17 @@ class Groups::RunnersController < Groups::ApplicationController before_action :authorize_admin_group_runners!, only: [:edit, :update, :destroy, :pause, :resume] before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show] + before_action only: [:show] do + push_frontend_feature_flag(:enforce_runner_token_expires_at) + end + feature_category :runner urgency :low def index finder = Ci::RunnersFinder.new(current_user: current_user, params: { group: @group }) @group_runners_limited_count = finder.execute.except(:limit, :offset).page.total_count_with_limit(:all, limit: 1000) + @group_runner_registration_token = @group.runners_token if can?(current_user, :register_group_runners, group) Gitlab::Tracking.event(self.class.name, 'index', user: current_user, namespace: @group) end @@ -22,7 +27,7 @@ class Groups::RunnersController < Groups::ApplicationController end def update - if Ci::Runners::UpdateRunnerService.new(@runner).update(runner_params) + if Ci::Runners::UpdateRunnerService.new(@runner).execute(runner_params).success? redirect_to group_runner_path(@group, @runner), notice: _('Runner was successfully updated.') else render 'edit' diff --git a/app/controllers/groups/settings/applications_controller.rb b/app/controllers/groups/settings/applications_controller.rb index bfe61696e0f..3557d485422 100644 --- a/app/controllers/groups/settings/applications_controller.rb +++ b/app/controllers/groups/settings/applications_controller.rb @@ -16,7 +16,7 @@ module Groups end def show - @created = get_created_session + @created = get_created_session if Feature.disabled?('hash_oauth_secrets') end def edit @@ -28,9 +28,15 @@ module Groups if @application.persisted? flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) - set_created_session + if Feature.enabled?('hash_oauth_secrets') - redirect_to group_settings_application_url(@group, @application) + @created = true + render :show + else + set_created_session + + redirect_to group_settings_application_url(@group, @application) + end else set_index_vars render :index diff --git a/app/controllers/groups/settings/repository_controller.rb b/app/controllers/groups/settings/repository_controller.rb index b0431c31179..cb62ea2a543 100644 --- a/app/controllers/groups/settings/repository_controller.rb +++ b/app/controllers/groups/settings/repository_controller.rb @@ -5,8 +5,9 @@ module Groups class RepositoryController < Groups::ApplicationController layout 'group_settings' skip_cross_project_access_check :show - before_action :authorize_create_deploy_token! - before_action :define_deploy_token_variables + before_action :authorize_create_deploy_token!, only: :create_deploy_token + before_action :authorize_access!, only: :show + before_action :define_deploy_token_variables, if: -> { can?(current_user, :create_deploy_token, @group) } before_action do push_frontend_feature_flag(:ajax_new_deploy_token, @group) end @@ -16,13 +17,13 @@ module Groups def create_deploy_token result = Groups::DeployTokens::CreateService.new(@group, current_user, deploy_token_params).execute - @new_deploy_token = result[:deploy_token] if result[:status] == :success + @created_deploy_token = result[:deploy_token] respond_to do |format| format.json do # IMPORTANT: It's a security risk to expose the token value more than just once here! - json = API::Entities::DeployTokenWithToken.represent(@new_deploy_token).as_json + json = API::Entities::DeployTokenWithToken.represent(@created_deploy_token).as_json render json: json, status: result[:http_status] end format.html do @@ -31,6 +32,7 @@ module Groups end end else + @new_deploy_token = result[:deploy_token] respond_to do |format| format.json { render json: { message: result[:message] }, status: result[:http_status] } format.html do @@ -43,6 +45,10 @@ module Groups private + def authorize_access! + authorize_admin_group! + end + def define_deploy_token_variables @deploy_tokens = @group.deploy_tokens.active @@ -55,3 +61,5 @@ module Groups end end end + +Groups::Settings::RepositoryController.prepend_mod diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 32b187c3260..9316204d89c 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -49,9 +49,9 @@ class GroupsController < Groups::ApplicationController layout :determine_layout feature_category :subgroups, [ - :index, :new, :create, :show, :edit, :update, - :destroy, :details, :transfer, :activity - ] + :index, :new, :create, :show, :edit, :update, + :destroy, :details, :transfer, :activity + ] feature_category :team_planning, [:issues, :issues_calendar, :preview_markdown] feature_category :code_review, [:merge_requests, :unfoldered_environment_names] @@ -276,6 +276,7 @@ class GroupsController < Groups::ApplicationController :avatar, :description, :emails_disabled, + :show_diff_preview_in_email, :mentions_disabled, :lfs_enabled, :name, diff --git a/app/controllers/health_controller.rb b/app/controllers/health_controller.rb index 071378f266e..5fac7c0d663 100644 --- a/app/controllers/health_controller.rb +++ b/app/controllers/health_controller.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +# rubocop:disable Rails/ApplicationController class HealthController < ActionController::Base protect_from_forgery with: :exception, prepend: true include RequiresWhitelistedMonitoringClient @@ -11,13 +12,7 @@ class HealthController < ActionController::Base ALL_CHECKS = [ *CHECKS, Gitlab::HealthChecks::DbCheck, - Gitlab::HealthChecks::Redis::RedisCheck, - Gitlab::HealthChecks::Redis::CacheCheck, - Gitlab::HealthChecks::Redis::QueuesCheck, - Gitlab::HealthChecks::Redis::SharedStateCheck, - Gitlab::HealthChecks::Redis::TraceChunksCheck, - Gitlab::HealthChecks::Redis::RateLimitingCheck, - Gitlab::HealthChecks::Redis::SessionsCheck, + *Gitlab::HealthChecks::Redis::ALL_INSTANCE_CHECKS, Gitlab::HealthChecks::GitalyCheck ].freeze @@ -45,3 +40,4 @@ class HealthController < ActionController::Base render json: result.json, status: result.http_status end end +# rubocop:enable Rails/ApplicationController diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb index 1508531828d..9635e476510 100644 --- a/app/controllers/help_controller.rb +++ b/app/controllers/help_controller.rb @@ -12,8 +12,7 @@ class HelpController < ApplicationController YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m.freeze def index - # Remove YAML frontmatter so that it doesn't look weird - @help_index = File.read(path_to_doc('index.md')).sub(YAML_FRONT_MATTER_REGEXP, '') + @help_index = get_markdown_without_frontmatter(path_to_doc('index.md')) # Prefix Markdown links with `help/` unless they are external links. # '//' not necessarily part of URL, e.g., mailto:mail@example.com @@ -59,8 +58,25 @@ class HelpController < ApplicationController @instance_configuration = InstanceConfiguration.new end + def drawers + @clean_path = Rack::Utils.clean_path_info(params[:markdown_file]) + @path = path_to_doc("#{@clean_path}.md") + + if File.exist?(@path) + render :drawers, formats: :html, layout: false + else + head :not_found + end + end + private + # Remove YAML frontmatter so that it doesn't look weird + helper_method :get_markdown_without_frontmatter + def get_markdown_without_frontmatter(path) + File.read(path).gsub(YAML_FRONT_MATTER_REGEXP, '') + end + def redirect_to_documentation_website? Gitlab::UrlSanitizer.valid_web?(documentation_url) end @@ -100,8 +116,7 @@ class HelpController < ApplicationController path = path_to_doc("#{@path}.md") if File.exist?(path) - # Remove YAML frontmatter so that it doesn't look weird - @markdown = File.read(path).gsub(YAML_FRONT_MATTER_REGEXP, '') + @markdown = get_markdown_without_frontmatter(path) render :show, formats: :html else diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb index 9fcb8385312..58a985cbc46 100644 --- a/app/controllers/ide_controller.rb +++ b/app/controllers/ide_controller.rb @@ -13,6 +13,7 @@ class IdeController < ApplicationController push_frontend_feature_flag(:build_service_proxy) push_frontend_feature_flag(:schema_linting) push_frontend_feature_flag(:reject_unsigned_commits_by_gitlab) + push_frontend_feature_flag(:vscode_web_ide, current_user) define_index_vars end diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index 9cc58ce542c..8a3e6809736 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -130,7 +130,7 @@ class Import::GithubController < Import::BaseController if sanitized_filter_param client.search_repos_by_name(sanitized_filter_param, pagination_options)[:items] else - client.octokit.repos(nil, pagination_options) + client.repos(pagination_options) end else filtered(client.repos) diff --git a/app/controllers/jira_connect/oauth_callbacks_controller.rb b/app/controllers/jira_connect/oauth_callbacks_controller.rb index f603a563402..e1a47a12b6d 100644 --- a/app/controllers/jira_connect/oauth_callbacks_controller.rb +++ b/app/controllers/jira_connect/oauth_callbacks_controller.rb @@ -7,5 +7,7 @@ class JiraConnect::OauthCallbacksController < ApplicationController feature_category :integrations + skip_before_action :authenticate_user! + def index; end end diff --git a/app/controllers/jira_connect/subscriptions_controller.rb b/app/controllers/jira_connect/subscriptions_controller.rb index 623113f8413..9305f46c39e 100644 --- a/app/controllers/jira_connect/subscriptions_controller.rb +++ b/app/controllers/jira_connect/subscriptions_controller.rb @@ -64,10 +64,12 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController private def allow_self_managed_content_security_policy + return unless Feature.enabled?(:jira_connect_oauth_self_managed) + return unless current_jira_installation.instance_url? request.content_security_policy.directives['connect-src'] ||= [] - request.content_security_policy.directives['connect-src'] << Gitlab::Utils.append_path(current_jira_installation.instance_url, '/-/jira_connect/oauth_application_ids') + request.content_security_policy.directives['connect-src'].concat(allowed_instance_connect_src) end def create_service @@ -77,4 +79,11 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController def allow_rendering_in_iframe response.headers.delete('X-Frame-Options') end + + def allowed_instance_connect_src + [ + Gitlab::Utils.append_path(current_jira_installation.instance_url, '/-/jira_connect/'), + Gitlab::Utils.append_path(current_jira_installation.instance_url, '/api/') + ] + end end diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb index 84f5632854b..7211eebdb4b 100644 --- a/app/controllers/jwt_controller.rb +++ b/app/controllers/jwt_controller.rb @@ -78,7 +78,11 @@ class JwtController < ApplicationController end def additional_params - { scopes: scopes_param, deploy_token: @authentication_result.deploy_token }.compact + { + scopes: scopes_param, + deploy_token: @authentication_result.deploy_token, + auth_type: @authentication_result.type + }.compact end # We have to parse scope here, because Docker Client does not send an array of scopes, diff --git a/app/controllers/metrics_controller.rb b/app/controllers/metrics_controller.rb index a0c307a0a03..bfd6181a940 100644 --- a/app/controllers/metrics_controller.rb +++ b/app/controllers/metrics_controller.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +# rubocop:disable Rails/ApplicationController class MetricsController < ActionController::Base include RequiresWhitelistedMonitoringClient @@ -34,3 +35,4 @@ class MetricsController < ActionController::Base ) end end +# rubocop:enable Rails/ApplicationController diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index a996bad3fac..ff466fd5fbb 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -25,7 +25,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController end def show - @created = get_created_session + @created = get_created_session if Feature.disabled?('hash_oauth_secrets') end def create @@ -34,9 +34,14 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController if @application.persisted? flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) - set_created_session + if Feature.enabled?('hash_oauth_secrets') + @created = true + render :show + else + set_created_session - redirect_to oauth_application_url(@application) + redirect_to oauth_application_url(@application) + end else set_index_vars render :index diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb index 07d786ab060..8ed67c26f19 100644 --- a/app/controllers/profiles/personal_access_tokens_controller.rb +++ b/app/controllers/profiles/personal_access_tokens_controller.rb @@ -65,7 +65,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController add_pagination_headers(tokens) end - ::API::Entities::PersonalAccessTokenWithDetails.represent(tokens) + ::PersonalAccessTokenSerializer.new.represent(tokens) end def add_pagination_headers(relation) diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index dd1ac526b89..e3704b77adc 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -137,7 +137,7 @@ class ProfilesController < Profiles::ApplicationController :pronouns, :pronunciation, :validation_password, - status: [:emoji, :message, :availability] + status: [:emoji, :message, :availability, :clear_status_after] ] end diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index 5bfda526fb0..2a20c67a23d 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -23,11 +23,10 @@ class Projects::BlameController < Projects::ApplicationController environment_params[:find_latest] = true @environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last - blame_service = Projects::BlameService.new(@blob, @commit, params.permit(:page)) + blame_service = Projects::BlameService.new(@blob, @commit, params.permit(:page, :no_pagination)) @blame = Gitlab::View::Presenter::Factory.new(blame_service.blame, project: @project, path: @path, page: blame_service.page).fabricate! - - render locals: { blame_pagination: blame_service.pagination } + @blame_pagination = blame_service.pagination end end diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index 6160dafb177..63c1378ad11 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -5,13 +5,17 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController include ActionView::Helpers::TextHelper include CycleAnalyticsParams include GracefulTimeoutHandling - include RedisTracking + include ProductAnalyticsTracking extend ::Gitlab::Utils::Override before_action :authorize_read_cycle_analytics! before_action :load_value_stream, only: :show - track_redis_hll_event :show, name: 'p_analytics_valuestream' + track_custom_event :show, + name: 'p_analytics_valuestream', + action: 'perform_analytics_usage_action', + label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', + destinations: %i[redis_hll snowplow] feature_category :planning_analytics urgency :low @@ -54,4 +58,12 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController permissions: @cycle_analytics.permissions(user: current_user) } end + + def tracking_namespace_source + project.namespace + end + + def tracking_project_source + project + end end diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 7ef9fd9daed..4f037cc843e 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -5,7 +5,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController # into app/controllers/projects/metrics_dashboard_controller.rb # See https://gitlab.com/gitlab-org/gitlab/-/issues/226002 for more details. + MIN_SEARCH_LENGTH = 3 + include MetricsDashboard + include ProductAnalyticsTracking layout 'project' @@ -26,6 +29,18 @@ class Projects::EnvironmentsController < Projects::ApplicationController before_action :expire_etag_cache, only: [:index], unless: -> { request.format.json? } after_action :expire_etag_cache, only: [:cancel_auto_stop] + track_event :index, + :folder, + :show, + :new, + :edit, + :create, + :update, + :stop, + :cancel_auto_stop, + :terminal, + name: 'users_visiting_environments_pages' + feature_category :continuous_delivery urgency :low @@ -35,12 +50,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController respond_to do |format| format.html format.json do - @environments = project.environments - .with_state(params[:scope] || :available) + @environments = search_environments.with_state(params[:scope] || :available) + environments_count_by_state = search_environments.count_by_state Gitlab::PollingInterval.set_header(response, interval: 3_000) - environments_count_by_state = project.environments.count_by_state - render json: { environments: serialize_environments(request, response, params[:nested]), review_app: serialize_review_app, @@ -59,7 +72,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController respond_to do |format| format.html format.json do - folder_environments = project.environments.where(environment_type: params[:id]) + folder_environments = search_environments(type: params[:id]) + @environments = folder_environments.with_state(params[:scope] || :available) .order(:name) @@ -236,6 +250,16 @@ class Projects::EnvironmentsController < Projects::ApplicationController @environment ||= project.environments.find(params[:id]) end + def search_environments(type: nil) + search = params[:search] if params[:search] && params[:search].length >= MIN_SEARCH_LENGTH + + @search_environments ||= + Environments::EnvironmentsFinder.new(project, + current_user, + type: type, + search: search).execute + end + def metrics_params params.require([:start, :end]) end diff --git a/app/controllers/projects/google_cloud/base_controller.rb b/app/controllers/projects/google_cloud/base_controller.rb index d1eb86c5e49..dfb73821b0f 100644 --- a/app/controllers/projects/google_cloud/base_controller.rb +++ b/app/controllers/projects/google_cloud/base_controller.rb @@ -12,7 +12,7 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController def admin_project_google_cloud! unless can?(current_user, :admin_project_google_cloud, project) - track_event('admin_project_google_cloud!', 'error_access_denied', 'invalid_user') + track_event(:error_invalid_user) access_denied! end end @@ -20,11 +20,7 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController 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!', - 'error_access_denied', - { reason: 'google_oauth2_not_configured', config: config } - ) + track_event(:error_google_oauth2_not_enabled) access_denied! 'This GitLab instance not configured for Google Oauth2.' end end @@ -35,7 +31,7 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController enabled_for_project = Feature.enabled?(:incubation_5mp_google_cloud, project) feature_is_enabled = enabled_for_user || enabled_for_group || enabled_for_project unless feature_is_enabled - track_event('feature_flag_enabled!', 'error_access_denied', 'feature_flag_not_enabled') + track_event(:error_feature_flag_not_enabled) access_denied! end end @@ -69,16 +65,14 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at] 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) + def track_event(action, label = nil) + Gitlab::Tracking.event( + self.class.name, + action.to_s, + label: label, + project: project, + user: current_user + ) end def gcp_projects diff --git a/app/controllers/projects/google_cloud/configuration_controller.rb b/app/controllers/projects/google_cloud/configuration_controller.rb index 8d252c35031..06a6674d578 100644 --- a/app/controllers/projects/google_cloud/configuration_controller.rb +++ b/app/controllers/projects/google_cloud/configuration_controller.rb @@ -16,7 +16,7 @@ module Projects revokeOauthUrl: revoke_oauth_url } @js_data = js_data.to_json - track_event('configuration#index', 'success', js_data) + track_event(:render_page) end private diff --git a/app/controllers/projects/google_cloud/databases_controller.rb b/app/controllers/projects/google_cloud/databases_controller.rb index 7b1cf6e5ce1..8f7554f248b 100644 --- a/app/controllers/projects/google_cloud/databases_controller.rb +++ b/app/controllers/projects/google_cloud/databases_controller.rb @@ -3,14 +3,139 @@ module Projects module GoogleCloud class DatabasesController < Projects::GoogleCloud::BaseController + before_action :validate_gcp_token! + before_action :validate_product, only: :new + def index js_data = { configurationUrl: project_google_cloud_configuration_path(project), deploymentsUrl: project_google_cloud_deployments_path(project), - databasesUrl: project_google_cloud_databases_path(project) + databasesUrl: project_google_cloud_databases_path(project), + cloudsqlPostgresUrl: new_project_google_cloud_database_path(project, :postgres), + cloudsqlMysqlUrl: new_project_google_cloud_database_path(project, :mysql), + cloudsqlSqlserverUrl: new_project_google_cloud_database_path(project, :sqlserver), + cloudsqlInstances: ::GoogleCloud::GetCloudsqlInstancesService.new(project).execute, + emptyIllustrationUrl: ActionController::Base.helpers.image_path('illustrations/pipelines_empty.svg') } @js_data = js_data.to_json - track_event('databases#index', 'success', js_data) + + track_event(:render_page) + end + + def new + product = permitted_params[:product].to_sym + + @title = title(product) + + @js_data = { + gcpProjects: gcp_projects, + refs: refs, + cancelPath: project_google_cloud_databases_path(project), + formTitle: form_title(product), + formDescription: description(product), + databaseVersions: Projects::GoogleCloud::CloudsqlHelper::VERSIONS[product], + tiers: Projects::GoogleCloud::CloudsqlHelper::TIERS + }.to_json + + track_event(:render_form) + render template: 'projects/google_cloud/databases/cloudsql_form', formats: :html + end + + def create + enable_response = ::GoogleCloud::EnableCloudsqlService + .new(project, current_user, enable_service_params) + .execute + + if enable_response[:status] == :error + track_event(:error_enable_cloudsql_services) + flash[:error] = error_message(enable_response[:message]) + else + permitted_params = params.permit(:gcp_project, :ref, :database_version, :tier) + create_response = ::GoogleCloud::CreateCloudsqlInstanceService + .new(project, current_user, create_service_params(permitted_params)) + .execute + + if create_response[:status] == :error + track_event(:error_create_cloudsql_instance) + flash[:warning] = error_message(create_response[:message]) + else + track_event(:create_cloudsql_instance, permitted_params.to_s) + flash[:notice] = success_message + end + end + + redirect_to project_google_cloud_databases_path(project) + end + + private + + def enable_service_params + { google_oauth2_token: token_in_session } + end + + def create_service_params(permitted_params) + { + google_oauth2_token: token_in_session, + gcp_project_id: permitted_params[:gcp_project], + environment_name: permitted_params[:ref], + database_version: permitted_params[:database_version], + tier: permitted_params[:tier] + } + end + + def error_message(message) + format(s_("CloudSeed|Google Cloud Error - %{message}"), message: message) + end + + def success_message + s_('CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes.') + end + + def validate_product + not_found unless permitted_params[:product].in?(%w[postgres mysql sqlserver]) + end + + def permitted_params + params.permit(:product) + end + + def title(product) + case product + when :postgres + s_('CloudSeed|Create Postgres Instance') + when :mysql + s_('CloudSeed|Create MySQL Instance') + else + s_('CloudSeed|Create MySQL Instance') + end + end + + def form_title(product) + case product + when :postgres + s_('CloudSeed|Cloud SQL for Postgres') + when :mysql + s_('CloudSeed|Cloud SQL for MySQL') + else + s_('CloudSeed|Cloud SQL for SQL Server') + end + end + + def description(product) + case product + when :postgres + s_('CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. '\ + 'Google handles replication, patch management, and database management '\ + 'to ensure availability and performance.') + when :mysql + s_('Cloud SQL instances are fully managed, relational MySQL databases. '\ + 'Google handles replication, patch management, and database management '\ + 'to ensure availability and performance.') + else + s_('Cloud SQL instances are fully managed, relational SQL Server databases. ' \ + 'Google handles replication, patch management, and database management ' \ + 'to ensure availability and performance.') + end end end end diff --git a/app/controllers/projects/google_cloud/deployments_controller.rb b/app/controllers/projects/google_cloud/deployments_controller.rb index 1ac4697a63f..f6cc8d5eafb 100644 --- a/app/controllers/projects/google_cloud/deployments_controller.rb +++ b/app/controllers/projects/google_cloud/deployments_controller.rb @@ -12,7 +12,7 @@ class Projects::GoogleCloud::DeploymentsController < Projects::GoogleCloud::Base enableCloudStorageUrl: project_google_cloud_deployments_cloud_storage_path(project) } @js_data = js_data.to_json - track_event('deployments#index', 'success', js_data) + track_event(:render_page) end def cloud_run @@ -21,7 +21,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', 'error_enable_cloud_run', enable_cloud_run_response) + track_event(:error_enable_services) flash[:error] = enable_cloud_run_response[:message] redirect_to project_google_cloud_deployments_path(project) else @@ -30,17 +30,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', 'error_generate_pipeline', generate_pipeline_response) + track_event(:error_generate_cloudrun_pipeline) flash[:error] = 'Failed to generate pipeline' redirect_to project_google_cloud_deployments_path(project) else cloud_run_mr_params = cloud_run_mr_params(generate_pipeline_response[:branch_name]) - track_event('deployments#cloud_run', 'success', cloud_run_mr_params) + track_event(:generate_cloudrun_pipeline) redirect_to project_new_merge_request_path(project, merge_request: cloud_run_mr_params) end end - rescue Google::Apis::ClientError, Google::Apis::ServerError, Google::Apis::AuthorizationError => e - track_event('deployments#cloud_run', 'error_gcp', e) + rescue Google::Apis::Error => e + track_event(:error_google_api) flash[:warning] = _('Google Cloud Error - %{error}') % { error: e } redirect_to project_google_cloud_deployments_path(project) end diff --git a/app/controllers/projects/google_cloud/gcp_regions_controller.rb b/app/controllers/projects/google_cloud/gcp_regions_controller.rb index 39f33624804..2f0bc05030f 100644 --- a/app/controllers/projects/google_cloud/gcp_regions_controller.rb +++ b/app/controllers/projects/google_cloud/gcp_regions_controller.rb @@ -15,13 +15,13 @@ class Projects::GoogleCloud::GcpRegionsController < Projects::GoogleCloud::BaseC cancelPath: project_google_cloud_configuration_path(project) } @js_data = js_data.to_json - track_event('gcp_regions#index', 'success', js_data) + track_event(:render_form) 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', 'success', response) + GoogleCloud::GcpRegionAddOrReplaceService.new(project).execute(permitted_params[:ref], permitted_params[:gcp_region]) + track_event(:configure_region) redirect_to project_google_cloud_configuration_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 index 1a9a2daf4f2..dbf91806722 100644 --- a/app/controllers/projects/google_cloud/revoke_oauth_controller.rb +++ b/app/controllers/projects/google_cloud/revoke_oauth_controller.rb @@ -9,10 +9,10 @@ class Projects::GoogleCloud::RevokeOauthController < Projects::GoogleCloud::Base if response.success? redirect_message = { notice: s_('GoogleCloud|Google OAuth2 token revocation requested') } - track_event('revoke_oauth#create', 'success', response.to_json) + track_event(:revoke_oauth) else redirect_message = { alert: s_('GoogleCloud|Google OAuth2 token revocation request failed') } - track_event('revoke_oauth#create', 'error', response.to_json) + track_event(:error) end session.delete(GoogleApi::CloudPlatform::Client.session_key_for_token) diff --git a/app/controllers/projects/google_cloud/service_accounts_controller.rb b/app/controllers/projects/google_cloud/service_accounts_controller.rb index 7f25054177e..89d624764df 100644 --- a/app/controllers/projects/google_cloud/service_accounts_controller.rb +++ b/app/controllers/projects/google_cloud/service_accounts_controller.rb @@ -5,7 +5,7 @@ class Projects::GoogleCloud::ServiceAccountsController < Projects::GoogleCloud:: def index if gcp_projects.empty? - track_event('service_accounts#index', 'error_form', 'no_gcp_projects') + track_event(:error_no_gcp_projects) flash[:warning] = _('No Google Cloud projects - You need at least one Google Cloud project') redirect_to project_google_cloud_configuration_path(project) else @@ -16,10 +16,10 @@ class Projects::GoogleCloud::ServiceAccountsController < Projects::GoogleCloud:: } @js_data = js_data.to_json - track_event('service_accounts#index', 'success', js_data) + track_event(:render_form) end - rescue Google::Apis::ClientError, Google::Apis::ServerError, Google::Apis::AuthorizationError => e - track_event('service_accounts#index', 'error_gcp', e) + rescue Google::Apis::Error => e + track_event(:error_google_api) flash[:warning] = _('Google Cloud Error - %{error}') % { error: e } redirect_to project_google_cloud_configuration_path(project) end @@ -35,10 +35,10 @@ class Projects::GoogleCloud::ServiceAccountsController < Projects::GoogleCloud:: environment_name: permitted_params[:ref] ).execute - track_event('service_accounts#create', 'success', response) + track_event(:create_service_account) redirect_to project_google_cloud_configuration_path(project), notice: response.message - rescue Google::Apis::ClientError, Google::Apis::ServerError, Google::Apis::AuthorizationError => e - track_event('service_accounts#create', 'error_gcp', e) + rescue Google::Apis::Error => e + track_event(:error_google_api) flash[:warning] = _('Google Cloud Error - %{error}') % { error: e } redirect_to project_google_cloud_configuration_path(project) end diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index 63309cce1e5..47557133ac8 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -2,14 +2,18 @@ class Projects::GraphsController < Projects::ApplicationController include ExtractsPath - include RedisTracking + include ProductAnalyticsTracking # Authorize before_action :require_non_empty_project before_action :assign_ref_vars before_action :authorize_read_repository_graphs! - track_redis_hll_event :charts, name: 'p_analytics_repo' + track_custom_event :charts, + name: 'p_analytics_repo', + action: 'perform_analytics_usage_action', + label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', + destinations: %i[redis_hll snowplow] feature_category :source_code_management, [:show, :commits, :languages, :charts] urgency :low, [:show] @@ -102,6 +106,14 @@ class Projects::GraphsController < Projects::ApplicationController render json: @log.to_json end + + def tracking_namespace_source + project.namespace + end + + def tracking_project_source + project + end end Projects::GraphsController.prepend_mod diff --git a/app/controllers/projects/hook_logs_controller.rb b/app/controllers/projects/hook_logs_controller.rb index 0ca3d71f728..3ab4c34737d 100644 --- a/app/controllers/projects/hook_logs_controller.rb +++ b/app/controllers/projects/hook_logs_controller.rb @@ -1,40 +1,19 @@ # frozen_string_literal: true class Projects::HookLogsController < Projects::ApplicationController - include ::Integrations::HooksExecution - before_action :authorize_admin_project! - before_action :hook, only: [:show, :retry] - before_action :hook_log, only: [:show, :retry] - - respond_to :html + include WebHooks::HookLogActions layout 'project_settings' - feature_category :integrations - urgency :low, [:retry] - - def show - end - - def retry - execute_hook - redirect_to edit_project_hook_path(@project, @hook) - end - private - def execute_hook - result = hook.execute(hook_log.request_data, hook_log.trigger) - set_hook_execution_notice(result) - end - def hook @hook ||= @project.hooks.find(params[:hook_id]) end - def hook_log - @hook_log ||= hook.web_hook_logs.find(params[:id]) + def after_retry_redirect_path + edit_project_hook_path(@project, hook) end end diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 50f388324f1..22b6bf6faf0 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Projects::HooksController < Projects::ApplicationController - include ::Integrations::HooksExecution + include ::WebHooks::HookActions # Authorize before_action :authorize_admin_project! @@ -35,7 +35,7 @@ class Projects::HooksController < Projects::ApplicationController end def hook_logs - @hook_logs ||= hook.web_hook_logs.recent.page(params[:page]) + @hook_logs ||= hook.web_hook_logs.recent.page(params[:page]).without_count end def trigger_values diff --git a/app/controllers/projects/incidents_controller.rb b/app/controllers/projects/incidents_controller.rb index 36b52533e78..cbf0c756e1e 100644 --- a/app/controllers/projects/incidents_controller.rb +++ b/app/controllers/projects/incidents_controller.rb @@ -11,6 +11,7 @@ class Projects::IncidentsController < Projects::ApplicationController push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?) push_frontend_feature_flag(:work_items_hierarchy, @project) + push_frontend_feature_flag(:remove_user_attributes_projects, @project) end feature_category :incident_management @@ -28,7 +29,7 @@ class Projects::IncidentsController < Projects::ApplicationController .inc_relations_for_view .iid_in(params[:id]) .without_order - .first + .take # rubocop:disable CodeReuse/ActiveRecord end end diff --git a/app/controllers/projects/integrations/shimos_controller.rb b/app/controllers/projects/integrations/shimos_controller.rb index 827dbb8f3f9..6c8313d0805 100644 --- a/app/controllers/projects/integrations/shimos_controller.rb +++ b/app/controllers/projects/integrations/shimos_controller.rb @@ -12,7 +12,7 @@ module Projects private def ensure_renderable - render_404 unless Feature.enabled?(:shimo_integration, project) && project.has_shimo? && project.shimo_integration&.render? + render_404 unless project.has_shimo? && project.shimo_integration&.render? end end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index d19db2b11ab..800a7df2566 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -42,6 +42,7 @@ class Projects::IssuesController < Projects::ApplicationController before_action do push_frontend_feature_flag(:incident_timeline, project) + push_frontend_feature_flag(:remove_user_attributes_projects, project) end before_action only: [:index, :show] do @@ -53,6 +54,7 @@ class Projects::IssuesController < Projects::ApplicationController push_frontend_feature_flag(:realtime_labels, project) push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?) push_frontend_feature_flag(:work_items_hierarchy, project) + push_frontend_feature_flag(:epic_widget_edit_confirmation, project) push_force_frontend_feature_flag(:work_items_create_from_markdown, project&.work_items_create_from_markdown_feature_flag_enabled?) end @@ -63,19 +65,19 @@ class Projects::IssuesController < Projects::ApplicationController alias_method :designs, :show feature_category :team_planning, [ - :index, :calendar, :show, :new, :create, :edit, :update, - :destroy, :move, :reorder, :designs, :toggle_subscription, - :discussions, :bulk_update, :realtime_changes, - :toggle_award_emoji, :mark_as_spam, :related_branches, - :can_create_branch, :create_merge_request - ] + :index, :calendar, :show, :new, :create, :edit, :update, + :destroy, :move, :reorder, :designs, :toggle_subscription, + :discussions, :bulk_update, :realtime_changes, + :toggle_award_emoji, :mark_as_spam, :related_branches, + :can_create_branch, :create_merge_request + ] urgency :low, [ - :index, :calendar, :show, :new, :create, :edit, :update, - :destroy, :move, :reorder, :designs, :toggle_subscription, - :discussions, :bulk_update, :realtime_changes, - :toggle_award_emoji, :mark_as_spam, :related_branches, - :can_create_branch, :create_merge_request - ] + :index, :calendar, :show, :new, :create, :edit, :update, + :destroy, :move, :reorder, :designs, :toggle_subscription, + :discussions, :bulk_update, :realtime_changes, + :toggle_award_emoji, :mark_as_spam, :related_branches, + :can_create_branch, :create_merge_request + ] feature_category :service_desk, [:service_desk] urgency :low, [:service_desk] diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index 7878ace5015..557ac566733 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -20,6 +20,9 @@ class Projects::JobsController < Projects::ApplicationController before_action :verify_proxy_request!, only: :proxy_websocket_authorize before_action :push_job_log_jump_to_failures, only: [:show] before_action :reject_if_build_artifacts_size_refreshing!, only: [:erase] + before_action do + push_frontend_feature_flag(:graphql_job_app, project, type: :development) + end layout 'project' @@ -120,11 +123,13 @@ class Projects::JobsController < Projects::ApplicationController end def erase - if @build.erase(erased_by: current_user) + service_response = Ci::BuildEraseService.new(@build, current_user).execute + + if service_response.success? redirect_to project_job_path(project, @build), notice: _("Job has been successfully erased!") else - respond_422 + head service_response.http_status end end diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index 279fd4c457e..a68c2ffa06d 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -29,6 +29,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic render_diffs end + # rubocop: disable Metrics/AbcSize def diffs_batch diff_options_hash = diff_options diff_options_hash[:paths] = params[:paths] if params[:paths] @@ -61,21 +62,11 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic options[:allow_tree_conflicts] ] - if Feature.enabled?(:etag_merge_request_diff_batches, @merge_request.project) - return unless stale?(etag: [cache_context + diff_options_hash.fetch(:paths, []), diffs]) - end + return unless stale?(etag: [cache_context + diff_options_hash.fetch(:paths, []), diffs]) - if diff_options_hash[:paths].blank? - render_cached( - diffs, - with: PaginatedDiffSerializer.new(current_user: current_user), - cache_context: -> (_) { [Digest::SHA256.hexdigest(cache_context.to_s)] }, - **options - ) - else - render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options) - end + render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options) end + # rubocop: enable Metrics/AbcSize def diffs_metadata diffs = @compare.diffs(diff_options) diff --git a/app/controllers/projects/merge_requests/drafts_controller.rb b/app/controllers/projects/merge_requests/drafts_controller.rb index ff6b6bfaf27..74bb3ad1a63 100644 --- a/app/controllers/projects/merge_requests/drafts_controller.rb +++ b/app/controllers/projects/merge_requests/drafts_controller.rb @@ -49,8 +49,24 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli def publish result = DraftNotes::PublishService.new(merge_request, current_user).execute(draft_note(allow_nil: true)) - if Feature.enabled?(:mr_review_submit_comment, @project) && create_note_params[:note] - Notes::CreateService.new(@project, current_user, create_note_params).execute + if Feature.enabled?(:mr_review_submit_comment, @project) + if create_note_params[:note] + ::Notes::CreateService.new(@project, current_user, create_note_params).execute + + merge_request_activity_counter.track_submit_review_comment(user: current_user) + end + + if Gitlab::Utils.to_boolean(approve_params[:approve]) + unless merge_request.approved_by?(current_user) + success = ::MergeRequests::ApprovalService.new(project: @project, current_user: current_user, params: approve_params).execute(merge_request) + + unless success + return render json: { message: _('An error occurred while approving, please try again.') }, status: :internal_server_error + end + end + + merge_request_activity_counter.track_submit_review_approve(user: current_user) + end end if result[:status] == :success @@ -115,6 +131,10 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli end end + def approve_params + params.permit(:approve) + end + def prepare_notes_for_rendering(notes) return [] unless notes @@ -147,4 +167,10 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli def authorize_create_note! access_denied! unless can?(current_user, :create_note, merge_request) end + + def merge_request_activity_counter + Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter + end end + +Projects::MergeRequests::DraftsController.prepend_mod diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 870c57fd6f3..5a212e9a152 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -45,6 +45,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:paginated_mr_discussions, project) push_frontend_feature_flag(:mr_review_submit_comment, project) push_frontend_feature_flag(:mr_experience_survey, project) + push_frontend_feature_flag(:remove_user_attributes_projects, @project) end before_action do @@ -56,11 +57,11 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo after_action :log_merge_request_show, only: [:show] feature_category :code_review, [ - :assign_related_issues, :bulk_update, :cancel_auto_merge, - :commit_change_content, :commits, :context_commits, :destroy, - :discussions, :edit, :index, :merge, :rebase, :remove_wip, - :show, :toggle_award_emoji, :toggle_subscription, :update - ] + :assign_related_issues, :bulk_update, :cancel_auto_merge, + :commit_change_content, :commits, :context_commits, :destroy, + :discussions, :edit, :index, :merge, :rebase, :remove_wip, + :show, :toggle_award_emoji, :toggle_subscription, :update + ] feature_category :code_testing, [:test_reports, :coverage_reports] feature_category :code_quality, [:codequality_reports, :codequality_mr_diff_reports] @@ -219,7 +220,13 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo def context_commits # 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 + commits = ContextCommitsFinder.new(project, @merge_request, { + search: params[:search], + author: params[:author], + committed_before: convert_date_to_epoch(params[:committed_before]), + committed_after: convert_date_to_epoch(params[:committed_after]), + limit: params[:limit] + }).execute render json: CommitEntity.represent(commits, { type: :full, request: merge_request }) end @@ -552,6 +559,11 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo diffs_metadata_project_json_merge_request_path(project, merge_request, 'json', params) end + + def convert_date_to_epoch(date) + Date.strptime(date, "%Y-%m-%d")&.to_time&.to_i if date + rescue Date::Error, TypeError + end end Projects::MergeRequestsController.prepend_mod_with('Projects::MergeRequestsController') diff --git a/app/controllers/projects/packages/package_files_controller.rb b/app/controllers/projects/packages/package_files_controller.rb index 32aadb4fcf4..1aa91ee1189 100644 --- a/app/controllers/projects/packages/package_files_controller.rb +++ b/app/controllers/projects/packages/package_files_controller.rb @@ -11,6 +11,7 @@ module Projects def download package_file = project.package_files.find(params[:id]) + package_file.package.touch_last_downloaded_at send_upload(package_file.file, attachment: package_file.file_name) end end diff --git a/app/controllers/projects/pipelines/tests_controller.rb b/app/controllers/projects/pipelines/tests_controller.rb index 8ac370b1bd4..d77cf095a4f 100644 --- a/app/controllers/projects/pipelines/tests_controller.rb +++ b/app/controllers/projects/pipelines/tests_controller.rb @@ -51,7 +51,8 @@ module Projects def test_suite suite = builds.sum do |build| - build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) + test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) + test_report.get_suite(build.test_suite_name) 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 b2aa1d9f4ca..2a8f7171f9c 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -3,6 +3,7 @@ class Projects::PipelinesController < Projects::ApplicationController include ::Gitlab::Utils::StrongMemoize include RedisTracking + include ProductAnalyticsTracking include ProjectStatsRefreshConflictsGuard include ZuoraCSP @@ -25,6 +26,7 @@ class Projects::PipelinesController < Projects::ApplicationController before_action do push_frontend_feature_flag(:pipeline_tabs_vue, @project) + push_frontend_feature_flag(:run_pipeline_graphql, @project) end # Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596 @@ -32,8 +34,11 @@ class Projects::PipelinesController < Projects::ApplicationController around_action :allow_gitaly_ref_name_caching, only: [:index, :show] - # Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/345074 - track_redis_hll_event :charts, name: 'p_analytics_pipelines' + track_custom_event :charts, + name: 'p_analytics_pipelines', + action: 'perform_analytics_usage_action', + label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', + destinations: %i[redis_hll snowplow] track_redis_hll_event :charts, name: 'p_analytics_ci_cd_pipelines', if: -> { should_track_ci_cd_pipelines? } track_redis_hll_event :charts, name: 'p_analytics_ci_cd_deployment_frequency', if: -> { should_track_ci_cd_deployment_frequency? } @@ -46,10 +51,10 @@ class Projects::PipelinesController < Projects::ApplicationController POLLING_INTERVAL = 10_000 feature_category :continuous_integration, [ - :charts, :show, :config_variables, :stage, :cancel, :retry, - :builds, :dag, :failures, :status, - :index, :create, :new, :destroy - ] + :charts, :show, :config_variables, :stage, :cancel, :retry, + :builds, :dag, :failures, :status, + :index, :create, :new, :destroy + ] feature_category :code_testing, [:test_report] feature_category :build_artifacts, [:downloadable_artifacts] @@ -371,6 +376,14 @@ class Projects::PipelinesController < Projects::ApplicationController def should_track_ci_cd_change_failure_rate? params[:chart] == 'change-failure-rate' end + + def tracking_namespace_source + project.namespace + end + + def tracking_project_source + project + end end Projects::PipelinesController.prepend_mod_with('Projects::PipelinesController') diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index ba9576795ec..ee12b85b3a4 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -15,7 +15,7 @@ class Projects::RunnersController < Projects::ApplicationController end def update - if Ci::Runners::UpdateRunnerService.new(@runner).update(runner_params) + if Ci::Runners::UpdateRunnerService.new(@runner).execute(runner_params).success? redirect_to project_runner_path(@project, @runner), notice: _('Runner was successfully updated.') else render 'edit' @@ -31,7 +31,7 @@ class Projects::RunnersController < Projects::ApplicationController end def resume - if Ci::Runners::UpdateRunnerService.new(@runner).update(active: true) + if Ci::Runners::UpdateRunnerService.new(@runner).execute(active: true).success? redirect_to project_runners_path(@project), notice: _('Runner was successfully updated.') else redirect_to project_runners_path(@project), alert: _('Runner was not updated.') @@ -39,7 +39,7 @@ class Projects::RunnersController < Projects::ApplicationController end def pause - if Ci::Runners::UpdateRunnerService.new(@runner).update(active: false) + if Ci::Runners::UpdateRunnerService.new(@runner).execute(active: false).success? 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/settings/integration_hook_logs_controller.rb b/app/controllers/projects/settings/integration_hook_logs_controller.rb index 1e42fbce4c4..3a921ecad0d 100644 --- a/app/controllers/projects/settings/integration_hook_logs_controller.rb +++ b/app/controllers/projects/settings/integration_hook_logs_controller.rb @@ -7,13 +7,13 @@ module Projects before_action :integration, only: [:show, :retry] - def retry - execute_hook - redirect_to edit_project_settings_integration_path(@project, @integration) - end - private + override :after_retry_redirect_path + def after_retry_redirect_path + edit_project_settings_integration_path(@project, @integration) + end + def integration @integration ||= @project.find_or_initialize_integration(params[:integration_id]) end diff --git a/app/controllers/projects/settings/integrations_controller.rb b/app/controllers/projects/settings/integrations_controller.rb index 03ef434456f..2bbcd9fe20c 100644 --- a/app/controllers/projects/settings/integrations_controller.rb +++ b/app/controllers/projects/settings/integrations_controller.rb @@ -11,7 +11,7 @@ module Projects before_action :integration, only: [:edit, :update, :test] before_action :default_integration, only: [:edit, :update] before_action :web_hook_logs, only: [:edit, :update] - before_action -> { check_rate_limit!(:project_testing_integration, scope: [@project, current_user]) }, only: :test + before_action -> { check_test_rate_limit! }, only: :test respond_to :html @@ -124,7 +124,7 @@ module Projects def web_hook_logs return unless integration.try(:service_hook).present? - @web_hook_logs ||= integration.service_hook.web_hook_logs.recent.page(params[:page]) + @web_hook_logs ||= integration.service_hook.web_hook_logs.recent.page(params[:page]).without_count end def ensure_integration_enabled @@ -140,6 +140,15 @@ module Projects def use_inherited_settings?(attributes) default_integration && attributes[:inherit_from_id] == default_integration.id.to_s end + + def check_test_rate_limit! + check_rate_limit!(:project_testing_integration, scope: [@project, current_user]) do + render json: { + error: true, + message: _('This endpoint has been requested too many times. Try again later.') + }, status: :ok + end + end end end end diff --git a/app/controllers/projects/settings/merge_requests_controller.rb b/app/controllers/projects/settings/merge_requests_controller.rb new file mode 100644 index 00000000000..93e10695767 --- /dev/null +++ b/app/controllers/projects/settings/merge_requests_controller.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +module Projects + module Settings + class MergeRequestsController < Projects::ApplicationController + layout 'project_settings' + + before_action :merge_requests_enabled? + before_action :present_project, only: [:edit] + before_action :authorize_admin_project! + + feature_category :code_review + + def update + result = ::Projects::UpdateService.new(@project, current_user, project_params).execute + + if result[:status] == :success + flash[:notice] = format(_("Project '%{project_name}' was successfully updated."), project_name: @project.name) + redirect_to project_settings_merge_requests_path(@project) + else + # Refresh the repo in case anything changed + @repository = @project.repository.reset + + flash[:alert] = result[:message] + @project.reset + render 'show' + end + end + + private + + def merge_requests_enabled? + render_404 unless @project.merge_requests_enabled? + end + + def project_params + params.require(:project) + .permit(project_params_attributes) + end + + def project_setting_attributes + %i[ + squash_option + allow_editing_commit_messages + mr_default_target_self + ] + end + + def project_params_attributes + [ + :allow_merge_on_skipped_pipeline, + :resolve_outdated_diff_discussions, + :only_allow_merge_if_all_discussions_are_resolved, + :only_allow_merge_if_pipeline_succeeds, + :printing_merge_request_link_enabled, + :remove_source_branch_after_merge, + :merge_method, + :merge_commit_template_or_default, + :squash_commit_template_or_default, + :suggestion_commit_message + ] + [project_setting_attributes: project_setting_attributes] + end + end + end +end + +Projects::Settings::MergeRequestsController.prepend_mod_with('Projects::Settings::MergeRequestsController') diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb index a178b8f7aa3..43c6451577a 100644 --- a/app/controllers/projects/settings/repository_controller.rb +++ b/app/controllers/projects/settings/repository_controller.rb @@ -34,13 +34,13 @@ module Projects def create_deploy_token result = Projects::DeployTokens::CreateService.new(@project, current_user, deploy_token_params).execute - @new_deploy_token = result[:deploy_token] if result[:status] == :success + @created_deploy_token = result[:deploy_token] respond_to do |format| format.json do # IMPORTANT: It's a security risk to expose the token value more than just once here! - json = API::Entities::DeployTokenWithToken.represent(@new_deploy_token).as_json + json = API::Entities::DeployTokenWithToken.represent(@created_deploy_token).as_json render json: json, status: result[:http_status] end format.html do @@ -49,6 +49,7 @@ module Projects end end else + @new_deploy_token = result[:deploy_token] respond_to do |format| format.json { render json: { message: result[:message] }, status: result[:http_status] } format.html do diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb index a364668ea5f..8f4987a07f6 100644 --- a/app/controllers/projects/uploads_controller.rb +++ b/app/controllers/projects/uploads_controller.rb @@ -35,14 +35,4 @@ class Projects::UploadsController < Projects::ApplicationController Project.find_by_full_path("#{namespace}/#{id}") end - - # Overrides ApplicationController#build_canonical_path since there are - # multiple routes that match project uploads: - # https://gitlab.com/gitlab-org/gitlab/issues/196396 - def build_canonical_path(project) - return super unless action_name == 'show' - return super unless params[:secret] && params[:filename] - - show_namespace_project_uploads_url(project.namespace.to_param, project.to_param, params[:secret], params[:filename]) - end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 8a6bcb4b3fc..5ceedbc1e01 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -12,6 +12,8 @@ class ProjectsController < Projects::ApplicationController include SourcegraphDecorator include PlanningHierarchy + REFS_LIMIT = 100 + prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) } around_action :allow_gitaly_ref_name_caching, only: [:index, :show] @@ -54,9 +56,9 @@ class ProjectsController < Projects::ApplicationController layout :determine_layout feature_category :projects, [ - :index, :show, :new, :create, :edit, :update, :transfer, - :destroy, :archive, :unarchive, :toggle_star, :activity - ] + :index, :show, :new, :create, :edit, :update, :transfer, + :destroy, :archive, :unarchive, :toggle_star, :activity + ] feature_category :source_code_management, [:remove_fork, :housekeeping, :refs] feature_category :team_planning, [:preview_markdown, :new_issuable_address] @@ -309,6 +311,8 @@ class ProjectsController < Projects::ApplicationController find_tags = true find_commits = true + use_gitaly_pagination = Feature.enabled?(:use_gitaly_pagination_for_refs, @project) + unless find_refs.nil? find_branches = find_refs.include?('branches') find_tags = find_refs.include?('tags') @@ -318,13 +322,21 @@ class ProjectsController < Projects::ApplicationController options = {} if find_branches - branches = BranchesFinder.new(@repository, refs_params).execute.take(100).map(&:name) + branches = BranchesFinder.new(@repository, refs_params.merge(per_page: REFS_LIMIT)) + .execute(gitaly_pagination: use_gitaly_pagination) + .take(REFS_LIMIT) + .map(&:name) + options['Branches'] = branches end if find_tags && @repository.tag_count.nonzero? - tags = TagsFinder.new(@repository, refs_params).execute - options['Tags'] = tags.take(100).map(&:name) + tags = TagsFinder.new(@repository, refs_params.merge(per_page: REFS_LIMIT)) + .execute(gitaly_pagination: use_gitaly_pagination) + .take(REFS_LIMIT) + .map(&:name) + + options['Tags'] = tags end # If reference is commit id - we should add it to branch/tag selectbox @@ -430,6 +442,7 @@ class ProjectsController < Projects::ApplicationController if Feature.enabled?(:split_operations_visibility_permissions, project) %i[ environments_access_level feature_flags_access_level releases_access_level + monitor_access_level ] else %i[operations_access_level] diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 33d2c482795..0bd266bb490 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -32,6 +32,7 @@ class RegistrationsController < Devise::RegistrationsController def create set_user_state + token = set_custom_confirmation_token super do |new_user| accept_pending_invitations if new_user.persisted? @@ -39,6 +40,7 @@ class RegistrationsController < Devise::RegistrationsController persist_accepted_terms_if_required(new_user) set_role_required(new_user) track_experiment_event(new_user) + send_custom_confirmation_instructions(new_user, token) if pending_approval? NotificationService.new.new_instance_access_request(new_user) @@ -118,8 +120,10 @@ class RegistrationsController < Devise::RegistrationsController def after_inactive_sign_up_path_for(resource) Gitlab::AppLogger.info(user_created_message) return new_user_session_path(anchor: 'login-pane') if resource.blocked_pending_approval? + return dashboard_projects_path if Feature.enabled?(:soft_email_confirmation) + return identity_verification_redirect_path if custom_confirmation_enabled?(resource) - Feature.enabled?(:soft_email_confirmation) ? dashboard_projects_path : users_almost_there_path(email: resource.email) + users_almost_there_path(email: resource.email) end private @@ -236,6 +240,22 @@ class RegistrationsController < Devise::RegistrationsController # signing up and becoming users experiment(:logged_out_marketing_header, actor: new_user).track(:signed_up) if new_user.persisted? end + + def identity_verification_redirect_path + # overridden by EE module + end + + def custom_confirmation_enabled?(resource) + # overridden by EE module + end + + def set_custom_confirmation_token + # overridden by EE module + end + + def send_custom_confirmation_instructions(user, token) + # overridden by EE module + end end RegistrationsController.prepend_mod_with('RegistrationsController') diff --git a/app/controllers/repositories/git_http_client_controller.rb b/app/controllers/repositories/git_http_client_controller.rb index fbf5d82a45b..a5ca17db113 100644 --- a/app/controllers/repositories/git_http_client_controller.rb +++ b/app/controllers/repositories/git_http_client_controller.rb @@ -3,7 +3,7 @@ module Repositories class GitHttpClientController < Repositories::ApplicationController include ActionController::HttpAuthentication::Basic - include KerberosSpnegoHelper + include KerberosHelper include Gitlab::Utils::StrongMemoize attr_reader :authentication_result, :redirected_path @@ -49,7 +49,7 @@ module Repositories if handle_basic_authentication(login, password) return # Allow access end - elsif allow_kerberos_spnego_auth? && spnego_provided? + elsif allow_kerberos_auth? && spnego_provided? kerberos_user = find_kerberos_user if kerberos_user @@ -91,7 +91,7 @@ module Repositories def send_challenges challenges = [] challenges << 'Basic realm="GitLab"' if allow_basic_auth? - challenges << spnego_challenge if allow_kerberos_spnego_auth? + challenges << spnego_challenge if allow_kerberos_auth? headers['Www-Authenticate'] = challenges.join("\n") if challenges.any? end diff --git a/app/controllers/repositories/git_http_controller.rb b/app/controllers/repositories/git_http_controller.rb index c3c6a51239d..144ec4c0de9 100644 --- a/app/controllers/repositories/git_http_controller.rb +++ b/app/controllers/repositories/git_http_controller.rb @@ -83,7 +83,7 @@ module Repositories return if Gitlab::Database.read_only? return unless repo_type.project? - OnboardingProgressService.async(project.namespace_id).execute(action: :git_pull) + Onboarding::ProgressService.async(project.namespace_id).execute(action: :git_pull) return if Feature.enabled?(:disable_git_http_fetch_writes) diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 5843e13c7cd..9f87ad6aaf6 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -57,7 +57,25 @@ class SearchController < ApplicationController @search_highlight = @search_service.search_highlight end + Gitlab::Metrics::GlobalSearchSlis.record_apdex( + elapsed: @global_search_duration_s, + search_type: @search_type, + search_level: @search_level, + search_scope: @scope + ) + increment_search_counters + ensure + if @search_type + # If we raise an error somewhere in the @global_search_duration_s benchmark block, we will end up here + # with a 200 status code, but an empty @global_search_duration_s. + Gitlab::Metrics::GlobalSearchSlis.record_error_rate( + error: @global_search_duration_s.nil? || (status < 200 || status >= 400), + search_type: @search_type, + search_level: @search_level, + search_scope: @scope + ) + end end def count |