From 905c1110b08f93a19661cf42a276c7ea90d0a0ff Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 22 Oct 2019 11:31:16 +0000 Subject: Add latest changes from gitlab-org/gitlab@12-4-stable-ee --- .../admin/application_settings_controller.rb | 11 +++- app/controllers/admin/dashboard_controller.rb | 5 ++ app/controllers/admin/sessions_controller.rb | 33 +++++++++++ app/controllers/admin/users_controller.rb | 16 +++++ app/controllers/application_controller.rb | 31 +++++++++- app/controllers/boards/application_controller.rb | 2 +- app/controllers/boards/lists_controller.rb | 17 ++++-- app/controllers/clusters/clusters_controller.rb | 1 + app/controllers/concerns/cycle_analytics_params.rb | 25 +++++++- .../concerns/enforces_admin_authentication.rb | 12 +++- app/controllers/concerns/invisible_captcha.rb | 4 +- app/controllers/concerns/metrics_dashboard.rb | 63 ++++++++++++++++++++ app/controllers/concerns/milestone_actions.rb | 2 +- app/controllers/concerns/render_service_results.rb | 29 +++++++++ app/controllers/concerns/renders_assignees.rb | 7 +++ .../concerns/sessionless_authentication.rb | 10 ++++ app/controllers/concerns/uploads_actions.rb | 28 ++++----- app/controllers/dashboard/todos_controller.rb | 4 +- app/controllers/explore/snippets_controller.rb | 2 +- app/controllers/groups/boards_controller.rb | 3 + app/controllers/groups/milestones_controller.rb | 2 +- .../groups/registry/repositories_controller.rb | 39 +++++++++++++ .../groups/settings/ci_cd_controller.rb | 23 ++++++++ app/controllers/groups/uploads_controller.rb | 2 +- app/controllers/groups_controller.rb | 23 +++++--- app/controllers/health_controller.rb | 36 +++++------- app/controllers/help_controller.rb | 4 +- app/controllers/import/bitbucket_controller.rb | 8 ++- app/controllers/import/github_controller.rb | 21 ++++++- .../notification_settings_controller.rb | 4 +- app/controllers/oauth/applications_controller.rb | 1 + app/controllers/oauth/authorizations_controller.rb | 1 + app/controllers/omniauth_callbacks_controller.rb | 5 ++ app/controllers/profiles/groups_controller.rb | 2 +- .../profiles/notifications_controller.rb | 11 +++- app/controllers/profiles_controller.rb | 1 + app/controllers/projects/artifacts_controller.rb | 38 +++++++++++- app/controllers/projects/boards_controller.rb | 3 + app/controllers/projects/branches_controller.rb | 19 ++++++ app/controllers/projects/commits_controller.rb | 4 +- .../projects/cycle_analytics/events_controller.rb | 10 +--- .../projects/cycle_analytics_controller.rb | 8 +-- app/controllers/projects/deploy_keys_controller.rb | 14 ++++- app/controllers/projects/deployments_controller.rb | 4 +- .../environments/prometheus_api_controller.rb | 21 ++----- .../projects/environments_controller.rb | 52 +++-------------- .../projects/git_http_client_controller.rb | 2 - app/controllers/projects/git_http_controller.rb | 16 ++--- app/controllers/projects/grafana_api_controller.rb | 25 ++++++++ app/controllers/projects/issues_controller.rb | 4 ++ app/controllers/projects/jobs_controller.rb | 41 +++++-------- app/controllers/projects/lfs_api_controller.rb | 17 +++++- .../merge_requests/application_controller.rb | 6 +- .../projects/merge_requests/diffs_controller.rb | 43 ++++++++++++-- .../projects/merge_requests_controller.rb | 12 +++- app/controllers/projects/pipelines_controller.rb | 25 +++++++- .../projects/protected_branches_controller.rb | 12 ++-- .../projects/registry/repositories_controller.rb | 2 + .../projects/registry/tags_controller.rb | 38 +++++------- app/controllers/projects/releases_controller.rb | 3 + .../projects/settings/ci_cd_controller.rb | 10 +++- .../projects/settings/operations_controller.rb | 9 ++- app/controllers/projects/templates_controller.rb | 8 +++ app/controllers/projects/uploads_controller.rb | 2 +- app/controllers/projects_controller.rb | 1 + app/controllers/registrations_controller.rb | 68 ++++++++++++++++++++-- app/controllers/sessions_controller.rb | 10 +++- app/controllers/uploads_controller.rb | 9 ++- 68 files changed, 777 insertions(+), 247 deletions(-) create mode 100644 app/controllers/admin/sessions_controller.rb create mode 100644 app/controllers/concerns/metrics_dashboard.rb create mode 100644 app/controllers/concerns/render_service_results.rb create mode 100644 app/controllers/concerns/renders_assignees.rb create mode 100644 app/controllers/groups/registry/repositories_controller.rb create mode 100644 app/controllers/projects/grafana_api_controller.rb (limited to 'app/controllers') diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index fbf63997b15..f7e33c09928 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -6,9 +6,9 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController before_action :set_application_setting before_action :whitelist_query_limiting, only: [:usage_data] - VALID_SETTING_PANELS = %w(general integrations repository templates + VALID_SETTING_PANELS = %w(general integrations repository ci_cd reporting metrics_and_profiling - network geo preferences).freeze + network preferences).freeze VALID_SETTING_PANELS.each do |action| define_method(action) { perform_update if submitted? } @@ -145,10 +145,15 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController end def render_update_error - action = VALID_SETTING_PANELS.include?(action_name) ? action_name : :general + action = valid_setting_panels.include?(action_name) ? action_name : :general render action end + + # overridden in EE + def valid_setting_panels + VALID_SETTING_PANELS + end end Admin::ApplicationSettingsController.prepend_if_ee('EE::Admin::ApplicationSettingsController') diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb index c36bbaab23b..f24ce9b5d03 100644 --- a/app/controllers/admin/dashboard_controller.rb +++ b/app/controllers/admin/dashboard_controller.rb @@ -2,6 +2,7 @@ class Admin::DashboardController < Admin::ApplicationController include CountHelper + helper_method :show_license_breakdown? COUNTED_ITEMS = [Project, User, Group].freeze @@ -13,6 +14,10 @@ class Admin::DashboardController < Admin::ApplicationController @groups = Group.order_id_desc.with_route.limit(10) end # rubocop: enable CodeReuse/ActiveRecord + + def show_license_breakdown? + false + end end Admin::DashboardController.prepend_if_ee('EE::Admin::DashboardController') diff --git a/app/controllers/admin/sessions_controller.rb b/app/controllers/admin/sessions_controller.rb new file mode 100644 index 00000000000..1f946e41995 --- /dev/null +++ b/app/controllers/admin/sessions_controller.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class Admin::SessionsController < ApplicationController + include InternalRedirect + + before_action :user_is_admin! + + def new + # Renders a form in which the admin can enter their password + end + + def create + if current_user_mode.enable_admin_mode!(password: params[:password]) + redirect_location = stored_location_for(:redirect) || admin_root_path + redirect_to safe_redirect_path(redirect_location) + else + flash.now[:alert] = _('Invalid Login or password') + render :new + end + end + + def destroy + current_user_mode.disable_admin_mode! + + redirect_to root_path, status: :found, notice: _('Admin mode disabled') + end + + private + + def user_is_admin! + render_404 unless current_user&.admin? + end +end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 61d36d1efc2..4c1ac8f206a 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -58,6 +58,22 @@ class Admin::UsersController < Admin::ApplicationController end end + def activate + return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user must be unblocked to be activated")) if user.blocked? + + user.activate + redirect_back_or_admin_user(notice: _("Successfully activated")) + end + + def deactivate + return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user cannot be deactivated")) if user.blocked? + return redirect_back_or_admin_user(notice: _("Successfully deactivated")) if user.deactivated? + return redirect_back_or_admin_user(notice: _("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated") % { minimum_inactive_days: ::User::MINIMUM_INACTIVE_DAYS }) unless user.can_be_deactivated? + + user.deactivate + redirect_back_or_admin_user(notice: _("Successfully deactivated")) + end + def block if update_user { |user| user.block } redirect_back_or_admin_user(notice: _("Successfully blocked")) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9a7859fc687..1443a71f6b1 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -13,6 +13,8 @@ class ApplicationController < ActionController::Base include WithPerformanceBar include SessionlessAuthentication include ConfirmEmailWarning + include Gitlab::Tracking::ControllerConcern + include Gitlab::Experimentation::ControllerConcern before_action :authenticate_user! before_action :enforce_terms!, if: :should_enforce_terms? @@ -24,8 +26,10 @@ class ApplicationController < ActionController::Base before_action :add_gon_variables, unless: [:peek_request?, :json_request?] before_action :configure_permitted_parameters, if: :devise_controller? before_action :require_email, unless: :devise_controller? + before_action :active_user_check, unless: :devise_controller? before_action :set_usage_stats_consent_flag before_action :check_impersonation_availability + before_action :require_role around_action :set_locale around_action :set_session_storage @@ -36,6 +40,7 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception, prepend: true helper_method :can? + helper_method :current_user_mode helper_method :import_sources_enabled?, :github_import_enabled?, :gitea_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, @@ -286,13 +291,19 @@ class ApplicationController < ActionController::Base def check_password_expiration return if session[:impersonator_id] || !current_user&.allow_password_authentication? - password_expires_at = current_user&.password_expires_at - - if password_expires_at && password_expires_at < Time.now + if current_user&.password_expired? return redirect_to new_profile_password_path end end + def active_user_check + return unless current_user && current_user.deactivated? + + sign_out current_user + flash[:alert] = _("Your account has been deactivated by your administrator. Please log back in to reactivate your account.") + redirect_to new_user_session_path + end + def ldap_security_check if current_user && current_user.requires_ldap_check? return unless current_user.try_obtain_ldap_lease @@ -533,6 +544,20 @@ class ApplicationController < ActionController::Base yield end end + + def current_user_mode + @current_user_mode ||= Gitlab::Auth::CurrentUserMode.new(current_user) + end + + # A user requires a role when they are part of the experimental signup flow (executed by the Growth team). Users + # are redirected to the welcome page when their role is required and the experiment is enabled for the current user. + def require_role + return unless current_user && current_user.role_required? && experiment_enabled?(:signup_flow) + + store_location_for :user, request.fullpath + + redirect_to users_sign_up_welcome_path + end end ApplicationController.prepend_if_ee('EE::ApplicationController') diff --git a/app/controllers/boards/application_controller.rb b/app/controllers/boards/application_controller.rb index eab908ba5ed..15ef6698472 100644 --- a/app/controllers/boards/application_controller.rb +++ b/app/controllers/boards/application_controller.rb @@ -13,7 +13,7 @@ module Boards end def board_parent - @board_parent ||= board.parent + @board_parent ||= board.resource_parent end def record_not_found(exception) diff --git a/app/controllers/boards/lists_controller.rb b/app/controllers/boards/lists_controller.rb index d0f4904c34e..880f7500708 100644 --- a/app/controllers/boards/lists_controller.rb +++ b/app/controllers/boards/lists_controller.rb @@ -9,13 +9,15 @@ module Boards skip_before_action :authenticate_user!, only: [:index] def index - lists = Boards::Lists::ListService.new(board.parent, current_user).execute(board) + lists = Boards::Lists::ListService.new(board.resource_parent, current_user).execute(board) + + List.preload_preferences_for_user(lists, current_user) render json: serialize_as_json(lists) end def create - list = Boards::Lists::CreateService.new(board.parent, current_user, create_list_params).execute(board) + list = Boards::Lists::CreateService.new(board.resource_parent, current_user, create_list_params).execute(board) if list.valid? render json: serialize_as_json(list) @@ -51,7 +53,10 @@ module Boards service = Boards::Lists::GenerateService.new(board_parent, current_user) if service.execute(board) - lists = board.lists.movable.preload_associations(current_user) + lists = board.lists.movable.preload_associations + + List.preload_preferences_for_user(lists, current_user) + render json: serialize_as_json(lists) else head :unprocessable_entity @@ -64,12 +69,16 @@ module Boards %i[label_id] end + def list_update_attrs + %i[collapsed position] + end + def create_list_params params.require(:list).permit(list_creation_attrs) end def update_list_params - params.require(:list).permit(:collapsed, :position) + params.require(:list).permit(list_update_attrs) end def serialize_as_json(resource) diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb index 15f1e8284ff..993aba661f3 100644 --- a/app/controllers/clusters/clusters_controller.rb +++ b/app/controllers/clusters/clusters_controller.rb @@ -170,6 +170,7 @@ class Clusters::ClustersController < Clusters::BaseController :zone, :num_nodes, :machine_type, + :cloud_run, :legacy_abac ]).merge( provider_type: :gcp, diff --git a/app/controllers/concerns/cycle_analytics_params.rb b/app/controllers/concerns/cycle_analytics_params.rb index b970bdc544e..1645af695be 100644 --- a/app/controllers/concerns/cycle_analytics_params.rb +++ b/app/controllers/concerns/cycle_analytics_params.rb @@ -3,8 +3,20 @@ module CycleAnalyticsParams extend ActiveSupport::Concern + def cycle_analytics_project_params + return {} unless params[:cycle_analytics].present? + + params[:cycle_analytics].permit(:start_date, :created_after, :created_before, :branch_name) + end + + def cycle_analytics_group_params + return {} unless params[:cycle_analytics].present? + + params[:cycle_analytics].permit(:start_date, :created_after, :created_before, project_ids: []) + end + def options(params) - @options ||= { from: start_date(params), current_user: current_user } + @options ||= { from: start_date(params), current_user: current_user }.merge(date_range(params)) end def start_date(params) @@ -17,6 +29,17 @@ module CycleAnalyticsParams 90.days.ago end end + + def date_range(params) + {}.tap do |date_range_params| + date_range_params[:from] = to_utc_time(params[:created_after]).beginning_of_day if params[:created_after] + date_range_params[:to] = to_utc_time(params[:created_before]).end_of_day if params[:created_before] + end.compact + end + + def to_utc_time(field) + Date.parse(field).to_time.utc + end end CycleAnalyticsParams.prepend_if_ee('EE::CycleAnalyticsParams') diff --git a/app/controllers/concerns/enforces_admin_authentication.rb b/app/controllers/concerns/enforces_admin_authentication.rb index 3ef92730df6..e731211f423 100644 --- a/app/controllers/concerns/enforces_admin_authentication.rb +++ b/app/controllers/concerns/enforces_admin_authentication.rb @@ -14,6 +14,16 @@ module EnforcesAdminAuthentication end def authenticate_admin! - render_404 unless current_user.admin? + return render_404 unless current_user.admin? + return unless Feature.enabled?(:user_mode_in_session) + + unless current_user_mode.admin_mode? + store_location_for(:redirect, request.fullpath) if storable_location? + redirect_to(new_admin_session_path, notice: _('Re-authentication required')) + end + end + + def storable_location? + request.path != new_admin_session_path end end diff --git a/app/controllers/concerns/invisible_captcha.rb b/app/controllers/concerns/invisible_captcha.rb index 45c0a5c58ef..d56f1d7fa5f 100644 --- a/app/controllers/concerns/invisible_captcha.rb +++ b/app/controllers/concerns/invisible_captcha.rb @@ -8,7 +8,7 @@ module InvisibleCaptcha end def on_honeypot_spam_callback - return unless Feature.enabled?(:invisible_captcha) + return unless Feature.enabled?(:invisible_captcha) || experiment_enabled?(:signup_flow) invisible_captcha_honeypot_counter.increment log_request('Invisible_Captcha_Honeypot_Request') @@ -17,7 +17,7 @@ module InvisibleCaptcha end def on_timestamp_spam_callback - return unless Feature.enabled?(:invisible_captcha) + return unless Feature.enabled?(:invisible_captcha) || experiment_enabled?(:signup_flow) invisible_captcha_timestamp_counter.increment log_request('Invisible_Captcha_Timestamp_Request') diff --git a/app/controllers/concerns/metrics_dashboard.rb b/app/controllers/concerns/metrics_dashboard.rb new file mode 100644 index 00000000000..62efdacb710 --- /dev/null +++ b/app/controllers/concerns/metrics_dashboard.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +# Provides an action which fetches a metrics dashboard according +# to the parameters specified by the controller. +module MetricsDashboard + extend ActiveSupport::Concern + + def metrics_dashboard + result = dashboard_finder.find( + project_for_dashboard, + current_user, + metrics_dashboard_params + ) + + if include_all_dashboards? + result[:all_dashboards] = dashboard_finder.find_all_paths(project_for_dashboard) + end + + respond_to do |format| + if result[:status] == :success + format.json { render dashboard_success_response(result) } + else + format.json { render dashboard_error_response(result) } + end + end + end + + private + + # Override in class to provide arguments to the finder. + def metrics_dashboard_params + {} + end + + # Override in class if response requires complete list of + # dashboards in addition to requested dashboard body. + def include_all_dashboards? + false + end + + def dashboard_finder + ::Gitlab::Metrics::Dashboard::Finder + end + + # Project is not defined for group and admin level clusters. + def project_for_dashboard + defined?(project) ? project : nil + end + + def dashboard_success_response(result) + { + status: :ok, + json: result.slice(:all_dashboards, :dashboard, :status) + } + end + + def dashboard_error_response(result) + { + status: result[:http_status], + json: result.slice(:all_dashboards, :message, :status) + } + end +end diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb index 1ead631663e..672d31ec779 100644 --- a/app/controllers/concerns/milestone_actions.rb +++ b/app/controllers/concerns/milestone_actions.rb @@ -35,7 +35,7 @@ module MilestoneActions render json: tabs_json("shared/milestones/_labels_tab", { labels: milestone_labels.map do |label| - label.present(issuable_subject: @milestone.parent) + label.present(issuable_subject: @milestone.resource_parent) end }) end diff --git a/app/controllers/concerns/render_service_results.rb b/app/controllers/concerns/render_service_results.rb new file mode 100644 index 00000000000..0149a71d9f5 --- /dev/null +++ b/app/controllers/concerns/render_service_results.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module RenderServiceResults + extend ActiveSupport::Concern + + def success_response(result) + render({ + status: result[:http_status], + json: result[:body] + }) + end + + def continue_polling_response + render({ + status: :no_content, + json: { + status: _('processing'), + message: _('Not ready yet. Try again later.') + } + }) + end + + def error_response(result) + render({ + status: result[:http_status] || :bad_request, + json: { status: result[:status], message: result[:message] } + }) + end +end diff --git a/app/controllers/concerns/renders_assignees.rb b/app/controllers/concerns/renders_assignees.rb new file mode 100644 index 00000000000..e9583a7a530 --- /dev/null +++ b/app/controllers/concerns/renders_assignees.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module RendersAssignees + def preload_assignees_for_render(merge_request) + merge_request.project.team.max_member_access_for_user_ids(merge_request.assignees.map(&:id)) + end +end diff --git a/app/controllers/concerns/sessionless_authentication.rb b/app/controllers/concerns/sessionless_authentication.rb index ba06384a37a..f644923443b 100644 --- a/app/controllers/concerns/sessionless_authentication.rb +++ b/app/controllers/concerns/sessionless_authentication.rb @@ -5,6 +5,12 @@ # Controller concern to handle PAT, RSS, and static objects token authentication methods # module SessionlessAuthentication + extend ActiveSupport::Concern + + included do + before_action :enable_admin_mode!, if: :sessionless_user? + end + # This filter handles personal access tokens, atom requests with rss tokens, and static object tokens def authenticate_sessionless_user!(request_format) user = Gitlab::Auth::RequestAuthenticator.new(request).find_sessionless_user(request_format) @@ -25,4 +31,8 @@ module SessionlessAuthentication sign_in(user, store: false, message: :sessionless_sign_in) end end + + def enable_admin_mode! + current_user_mode.enable_admin_mode!(skip_password_validation: true) if Feature.enabled?(:user_mode_in_session) + end end diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb index 60a68cec3c3..6d9ee39f841 100644 --- a/app/controllers/concerns/uploads_actions.rb +++ b/app/controllers/concerns/uploads_actions.rb @@ -29,15 +29,17 @@ module UploadsActions def show return render_404 unless uploader&.exists? - if cache_publicly? - # We need to reset caching from the applications controller to get rid of the no-store value - headers['Cache-Control'] = '' - expires_in 5.minutes, public: true, must_revalidate: false - else - expires_in 0.seconds, must_revalidate: true, private: true - end + # We need to reset caching from the applications controller to get rid of the no-store value + headers['Cache-Control'] = '' + headers['Pragma'] = '' + + ttl, directives = *cache_settings + ttl ||= 6.months + directives ||= { private: true, must_revalidate: true } + + expires_in ttl, directives - disposition = uploader.image_or_video? ? 'inline' : 'attachment' + disposition = uploader.embeddable? ? 'inline' : 'attachment' uploaders = [uploader, *uploader.versions.values] uploader = uploaders.find { |version| version.filename == params[:filename] } @@ -91,7 +93,7 @@ module UploadsActions upload_paths = uploader.upload_paths(params[:filename]) upload = Upload.find_by(model: model, uploader: uploader_class.to_s, path: upload_paths) - upload&.build_uploader + upload&.retrieve_uploader end # rubocop: enable CodeReuse/ActiveRecord @@ -112,16 +114,16 @@ module UploadsActions uploader end - def image_or_video? - uploader && uploader.exists? && uploader.image_or_video? + def embeddable? + uploader && uploader.exists? && uploader.embeddable? end def find_model nil end - def cache_publicly? - false + def cache_settings + [] end def model diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb index 7012bfcefe3..80c0a0d88a8 100644 --- a/app/controllers/dashboard/todos_controller.rb +++ b/app/controllers/dashboard/todos_controller.rb @@ -78,8 +78,8 @@ class Dashboard::TodosController < Dashboard::ApplicationController def todos_counts { - count: number_with_delimiter(current_user.todos_pending_count), - done_count: number_with_delimiter(current_user.todos_done_count) + count: current_user.todos_pending_count, + done_count: current_user.todos_done_count } end diff --git a/app/controllers/explore/snippets_controller.rb b/app/controllers/explore/snippets_controller.rb index d4c6aae2ca8..61068df77d1 100644 --- a/app/controllers/explore/snippets_controller.rb +++ b/app/controllers/explore/snippets_controller.rb @@ -5,7 +5,7 @@ class Explore::SnippetsController < Explore::ApplicationController include Gitlab::NoteableMetadata def index - @snippets = SnippetsFinder.new(current_user) + @snippets = SnippetsFinder.new(current_user, explore: true) .execute .page(params[:page]) .inc_author diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb index 40b8d5ed72c..3c86f3108ab 100644 --- a/app/controllers/groups/boards_controller.rb +++ b/app/controllers/groups/boards_controller.rb @@ -5,6 +5,9 @@ class Groups::BoardsController < Groups::ApplicationController include RecordUserLastActivity before_action :assign_endpoint_vars + before_action do + push_frontend_feature_flag(:multi_select_board) + end private diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 1eacae06457..1e9d51cf970 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -44,7 +44,7 @@ class Groups::MilestonesController < Groups::ApplicationController # all projects milestones states at once. milestones, update_params = get_milestones_for_update milestones.each do |milestone| - Milestones::UpdateService.new(milestone.parent, current_user, update_params).execute(milestone) + Milestones::UpdateService.new(milestone.resource_parent, current_user, update_params).execute(milestone) end redirect_to milestone_path diff --git a/app/controllers/groups/registry/repositories_controller.rb b/app/controllers/groups/registry/repositories_controller.rb new file mode 100644 index 00000000000..e09a9e6eb21 --- /dev/null +++ b/app/controllers/groups/registry/repositories_controller.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true +module Groups + module Registry + class RepositoriesController < Groups::ApplicationController + before_action :verify_container_registry_enabled! + before_action :authorize_read_container_image! + before_action :feature_flag_group_container_registry_browser! + + def index + track_event(:list_repositories) + + respond_to do |format| + format.html + format.json do + @images = group.container_repositories.with_api_entity_associations + + render json: ContainerRepositoriesSerializer + .new(current_user: current_user) + .represent(@images) + end + end + end + + private + + def feature_flag_group_container_registry_browser! + render_404 unless Feature.enabled?(:group_container_registry_browser, group) + end + + def verify_container_registry_enabled! + render_404 unless Gitlab.config.registry.enabled + end + + def authorize_read_container_image! + return render_404 unless can?(current_user, :read_container_image, group) + end + end + end +end diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb index c465e622de0..0e83d057484 100644 --- a/app/controllers/groups/settings/ci_cd_controller.rb +++ b/app/controllers/groups/settings/ci_cd_controller.rb @@ -5,11 +5,22 @@ module Groups class CiCdController < Groups::ApplicationController skip_cross_project_access_check :show before_action :authorize_admin_group! + before_action :authorize_update_max_artifacts_size!, only: [:update] def show define_ci_variables end + def update + if update_group_service.execute + flash[:notice] = s_('GroupSettings|Pipeline settings was updated for the group') + else + flash[:alert] = s_("GroupSettings|There was a problem updating the pipeline settings: %{error_messages}." % { error_messages: group.errors.full_messages }) + end + + redirect_to group_settings_ci_cd_path + end + def reset_registration_token @group.reset_runners_token! @@ -40,6 +51,10 @@ module Groups return render_404 unless can?(current_user, :admin_group, group) end + def authorize_update_max_artifacts_size! + return render_404 unless can?(current_user, :update_max_artifacts_size, group) + end + def auto_devops_params params.require(:group).permit(:auto_devops_enabled) end @@ -47,6 +62,14 @@ module Groups def auto_devops_service Groups::AutoDevopsService.new(group, current_user, auto_devops_params) end + + def update_group_service + Groups::UpdateService.new(group, current_user, update_group_params) + end + + def update_group_params + params.require(:group).permit(:max_artifacts_size) + end end end end diff --git a/app/controllers/groups/uploads_controller.rb b/app/controllers/groups/uploads_controller.rb index 7e5cdae0ce3..3ae7e36c740 100644 --- a/app/controllers/groups/uploads_controller.rb +++ b/app/controllers/groups/uploads_controller.rb @@ -4,7 +4,7 @@ class Groups::UploadsController < Groups::ApplicationController include UploadsActions include WorkhorseRequest - skip_before_action :group, if: -> { action_name == 'show' && image_or_video? } + skip_before_action :group, if: -> { action_name == 'show' && embeddable? } before_action :authorize_upload_file!, only: [:create, :authorize] before_action :verify_workhorse_api!, only: [:authorize] diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 95a7876a055..35e364abba3 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -104,7 +104,6 @@ class GroupsController < Groups::ApplicationController redirect_to edit_group_path(@group, anchor: params[:update_section]), notice: "Group '#{@group.name}' was successfully updated." else @group.path = @group.path_before_last_save || @group.path_was - render action: "edit" end end @@ -124,7 +123,7 @@ class GroupsController < Groups::ApplicationController flash[:notice] = "Group '#{@group.name}' was successfully transferred." redirect_to group_path(@group) else - flash[:alert] = service.error + flash[:alert] = service.error.html_safe redirect_to edit_group_path(@group) end end @@ -198,15 +197,13 @@ class GroupsController < Groups::ApplicationController def load_events params[:sort] ||= 'latest_activity_desc' - options = {} - options[:include_subgroups] = true - - @projects = GroupProjectsFinder.new(params: params, group: group, options: options, current_user: current_user) - .execute - .includes(:namespace) + options = { include_subgroups: true } + projects = GroupProjectsFinder.new(params: params, group: group, options: options, current_user: current_user) + .execute + .includes(:namespace) @events = EventCollection - .new(@projects, offset: params[:offset].to_i, filter: event_filter) + .new(projects, offset: params[:offset].to_i, filter: event_filter, groups: groups) .to_a Events::RenderService @@ -228,6 +225,14 @@ class GroupsController < Groups::ApplicationController url_for(safe_params) end + + private + + def groups + if @group.supports_events? + @group.self_and_descendants.public_or_visible_to_user(current_user) + end + end end GroupsController.prepend_if_ee('EE::GroupsController') diff --git a/app/controllers/health_controller.rb b/app/controllers/health_controller.rb index dc9a52f8da5..efd5f0fc607 100644 --- a/app/controllers/health_controller.rb +++ b/app/controllers/health_controller.rb @@ -14,35 +14,25 @@ class HealthController < ActionController::Base ].freeze def readiness - results = CHECKS.map { |check| [check.name, check.readiness] } - - render_check_results(results) + # readiness check is a collection with all above application-level checks + render_checks(*CHECKS) end def liveness - results = CHECKS.map { |check| [check.name, check.liveness] } - - render_check_results(results) + # liveness check is a collection without additional checks + render_checks end private - def render_check_results(results) - flattened = results.flat_map do |name, result| - if result.is_a?(Gitlab::HealthChecks::Result) - [[name, result]] - else - result.map { |r| [name, r] } - end - end - success = flattened.all? { |name, r| r.success } - - response = flattened.map do |name, r| - info = { status: r.success ? 'ok' : 'failed' } - info['message'] = r.message if r.message - info[:labels] = r.labels if r.labels - [name, info] - end - render json: response.to_h, status: success ? :ok : :service_unavailable + def render_checks(*checks) + result = Gitlab::HealthChecks::Probes::Collection + .new(*checks) + .execute + + # disable static error pages at the gitlab-workhorse level, we want to see this error response even in production + headers["X-GitLab-Custom-Error"] = 1 unless result.success? + + render json: result.json, status: result.http_status end end diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb index 837c26c630a..a58235790ad 100644 --- a/app/controllers/help_controller.rb +++ b/app/controllers/help_controller.rb @@ -40,8 +40,8 @@ class HelpController < ApplicationController end end - # Allow access to images in the doc folder - format.any(:png, :gif, :jpeg, :mp4) do + # Allow access to specific media files in the doc folder + format.any(:png, :gif, :jpeg, :mp4, :mp3) do # Note: We are purposefully NOT using `Rails.root.join` path = File.join(Rails.root, 'doc', "#{@path}.#{params[:format]}") diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb index 293d76ea765..c37e799de62 100644 --- a/app/controllers/import/bitbucket_controller.rb +++ b/app/controllers/import/bitbucket_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Import::BitbucketController < Import::BaseController + include ActionView::Helpers::SanitizeHelper + before_action :verify_bitbucket_import_enabled before_action :bitbucket_auth, except: :callback @@ -21,7 +23,7 @@ class Import::BitbucketController < Import::BaseController # rubocop: disable CodeReuse/ActiveRecord def status bitbucket_client = Bitbucket::Client.new(credentials) - repos = bitbucket_client.repos + repos = bitbucket_client.repos(filter: sanitized_filter_param) @repos, @incompatible_repos = repos.partition { |repo| repo.valid? } @@ -104,4 +106,8 @@ class Import::BitbucketController < Import::BaseController refresh_token: session[:bitbucket_refresh_token] } end + + def sanitized_filter_param + @filter ||= sanitize(params[:filter]) + end end diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index 72f830fc9a1..c418b11ab13 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -2,6 +2,7 @@ class Import::GithubController < Import::BaseController include ImportHelper + include ActionView::Helpers::SanitizeHelper before_action :verify_import_enabled before_action :provider_auth, only: [:status, :realtime_changes, :create] @@ -55,7 +56,7 @@ class Import::GithubController < Import::BaseController def realtime_changes Gitlab::PollingInterval.set_header(response, interval: 3_000) - render json: find_jobs(provider) + render json: already_added_projects.to_json(only: [:id], methods: [:import_status]) end private @@ -82,7 +83,7 @@ class Import::GithubController < Import::BaseController end def already_added_projects - @already_added_projects ||= find_already_added_projects(provider) + @already_added_projects ||= filtered(find_already_added_projects(provider)) end def already_added_project_names @@ -104,7 +105,7 @@ class Import::GithubController < Import::BaseController end def client_repos - @client_repos ||= client.repos + @client_repos ||= filtered(client.repos) end def verify_import_enabled @@ -185,6 +186,20 @@ class Import::GithubController < Import::BaseController def extra_import_params {} end + + def sanitized_filter_param + @filter ||= sanitize(params[:filter]) + end + + def filter_attribute + :name + end + + def filtered(collection) + return collection unless sanitized_filter_param + + collection.select { |item| item[filter_attribute].include?(sanitized_filter_param) } + end end Import::GithubController.prepend_if_ee('EE::Import::GithubController') diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb index 43c4f4d220e..c97fec0a6ee 100644 --- a/app/controllers/notification_settings_controller.rb +++ b/app/controllers/notification_settings_controller.rb @@ -50,8 +50,6 @@ class NotificationSettingsController < ApplicationController end def notification_setting_params_for(source) - allowed_fields = NotificationSetting.email_events(source).dup - allowed_fields << :level - params.require(:notification_setting).permit(allowed_fields) + params.require(:notification_setting).permit(NotificationSetting.allowed_fields(source)) end end diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index ab4ca56bb49..12dc2d1af1c 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -5,6 +5,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController include Gitlab::Allowable include PageLayoutHelper include OauthApplications + include Gitlab::Experimentation::ControllerConcern before_action :verify_user_oauth_applications_enabled, except: :index before_action :authenticate_user! diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb index 705389749d8..e65726dffbf 100644 --- a/app/controllers/oauth/authorizations_controller.rb +++ b/app/controllers/oauth/authorizations_controller.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController + include Gitlab::Experimentation::ControllerConcern layout 'profile' # Overridden from Doorkeeper::AuthorizationsController to diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 755ce3463c4..b992972dfb8 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -148,6 +148,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController if user.two_factor_enabled? && !auth_user.bypass_two_factor? prompt_for_two_factor(user) else + if user.deactivated? + user.activate + flash[:notice] = _('Welcome back! Your account had been deactivated due to inactivity but is now reactivated.') + end + sign_in_and_redirect(user, event: :authentication) end else diff --git a/app/controllers/profiles/groups_controller.rb b/app/controllers/profiles/groups_controller.rb index c755bcb718a..04b5ee270dc 100644 --- a/app/controllers/profiles/groups_controller.rb +++ b/app/controllers/profiles/groups_controller.rb @@ -5,7 +5,7 @@ class Profiles::GroupsController < Profiles::ApplicationController def update group = find_routable!(Group, params[:id]) - notification_setting = current_user.notification_settings.find_by(source: group) # rubocop: disable CodeReuse/ActiveRecord + notification_setting = current_user.notification_settings_for(group) if notification_setting.update(update_params) flash[:notice] = "Notification settings for #{group.name} saved" diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb index 617e5bb7cb3..5f44e55f3ef 100644 --- a/app/controllers/profiles/notifications_controller.rb +++ b/app/controllers/profiles/notifications_controller.rb @@ -3,9 +3,14 @@ class Profiles::NotificationsController < Profiles::ApplicationController # rubocop: disable CodeReuse/ActiveRecord def show - @user = current_user - @group_notifications = current_user.notification_settings.for_groups.order(:id) - @project_notifications = current_user.notification_settings.for_projects.order(:id) + @user = current_user + @group_notifications = current_user.notification_settings.for_groups.order(:id) + @group_notifications += GroupsFinder.new( + current_user, + all_available: false, + exclude_group_ids: @group_notifications.select(:source_id) + ).execute.map { |group| current_user.notification_settings_for(group, inherit: true) } + @project_notifications = current_user.notification_settings.for_projects.order(:id) @global_notification_setting = current_user.global_notification_setting end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 958a24b6c0e..2b7571e42b7 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -100,6 +100,7 @@ class ProfilesController < Profiles::ApplicationController :avatar, :bio, :email, + :role, :hide_no_password, :hide_no_ssh_key, :hide_project_limit, diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index da8a371acaa..50399a8cfbb 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -8,10 +8,37 @@ class Projects::ArtifactsController < Projects::ApplicationController layout 'project' before_action :authorize_read_build! before_action :authorize_update_build!, only: [:keep] + before_action :authorize_destroy_artifacts!, only: [:destroy] before_action :extract_ref_name_and_path - before_action :validate_artifacts!, except: [:download] + before_action :validate_artifacts!, except: [:index, :download, :destroy] before_action :entry, only: [:file] + MAX_PER_PAGE = 20 + + def index + # Loading artifacts is very expensive in projects with a lot of artifacts. + # This feature flag prevents a DOS attack vector. + # It should be removed only after resolving the underlying performance + # issues: https://gitlab.com/gitlab-org/gitlab/issues/32281 + return head :no_content unless Feature.enabled?(:artifacts_management_page, @project) + + finder = ArtifactsFinder.new(@project, artifacts_params) + all_artifacts = finder.execute + + @artifacts = all_artifacts.page(params[:page]).per(MAX_PER_PAGE) + @total_size = all_artifacts.total_size + end + + def destroy + notice = if artifact.destroy + _('Artifact was successfully deleted.') + else + _('Artifact could not be deleted.') + end + + redirect_to project_artifacts_path(@project), status: :see_other, notice: notice + end + def download return render_404 unless artifacts_file @@ -74,6 +101,10 @@ class Projects::ArtifactsController < Projects::ApplicationController @ref_name, @path = extract_ref(params[:ref_name_and_path]) end + def artifacts_params + params.permit(:sort) + end + def validate_artifacts! render_404 unless build&.artifacts? end @@ -85,6 +116,11 @@ class Projects::ArtifactsController < Projects::ApplicationController end end + def artifact + @artifact ||= + project.job_artifacts.find(params[:id]) + end + def build_from_id project.builds.find_by_id(params[:job_id]) if params[:job_id] end diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb index 14b02993e6e..3b335fa4af4 100644 --- a/app/controllers/projects/boards_controller.rb +++ b/app/controllers/projects/boards_controller.rb @@ -7,6 +7,9 @@ class Projects::BoardsController < Projects::ApplicationController before_action :check_issues_available! before_action :authorize_read_board!, only: [:index, :show] before_action :assign_endpoint_vars + before_action do + push_frontend_feature_flag(:multi_select_board) + end private diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index c125ed3605a..578a3d451a7 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -11,6 +11,7 @@ class Projects::BranchesController < Projects::ApplicationController # Support legacy URLs before_action :redirect_for_legacy_index_sort_or_search, only: [:index] + before_action :limit_diverging_commit_counts!, only: [:diverging_commit_counts] def index respond_to do |format| @@ -125,6 +126,24 @@ class Projects::BranchesController < Projects::ApplicationController private + # It can be expensive to calculate the diverging counts for each + # branch. Normally the frontend should be specifying a set of branch + # names, but prior to + # https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/32496, the + # frontend could omit this set. To prevent excessive I/O, we require + # that a list of names be specified. + def limit_diverging_commit_counts! + return unless Feature.enabled?(:limit_diverging_commit_counts, default_enabled: true) + + limit = Kaminari.config.default_per_page + + # If we don't have many branches in the repository, then go ahead. + return if project.repository.branch_count <= limit + return if params[:names].present? && Array(params[:names]).length <= limit + + render json: { error: "Specify at least one and at most #{limit} branch names" }, status: :unprocessable_entity + end + def ref if params[:ref] ref_escaped = strip_tags(sanitize(params[:ref])) diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index 76705b4410c..15bb35dd0be 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -72,7 +72,9 @@ class Projects::CommitsController < Projects::ApplicationController @repository.commits(@ref, path: @path, limit: @limit, offset: @offset) end - @commits = @commits.with_pipeline_status + @commits.each(&:lazy_author) # preload authors + + @commits = @commits.with_latest_pipeline(@ref) @commits = set_commits_for_rendering(@commits) end diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb index 926592b9681..673f53c221b 100644 --- a/app/controllers/projects/cycle_analytics/events_controller.rb +++ b/app/controllers/projects/cycle_analytics/events_controller.rb @@ -23,7 +23,7 @@ module Projects end def test - options(cycle_analytics_params)[:branch] = cycle_analytics_params[:branch_name] + options(cycle_analytics_project_params)[:branch] = cycle_analytics_project_params[:branch_name] render_events(cycle_analytics[:test].events) end @@ -50,13 +50,7 @@ module Projects end def cycle_analytics - @cycle_analytics ||= ::CycleAnalytics::ProjectLevel.new(project, options: options(cycle_analytics_params)) - end - - def cycle_analytics_params - return {} unless params[:cycle_analytics].present? - - params[:cycle_analytics].permit(:start_date, :branch_name) + @cycle_analytics ||= ::CycleAnalytics::ProjectLevel.new(project, options: options(cycle_analytics_project_params)) end end end diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index b9d7dbd37be..f13c75ac4cc 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -9,7 +9,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController before_action :authorize_read_cycle_analytics! def show - @cycle_analytics = ::CycleAnalytics::ProjectLevel.new(@project, options: options(cycle_analytics_params)) + @cycle_analytics = ::CycleAnalytics::ProjectLevel.new(@project, options: options(cycle_analytics_project_params)) @cycle_analytics_no_data = @cycle_analytics.no_stats? @@ -27,12 +27,6 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController private - def cycle_analytics_params - return {} unless params[:cycle_analytics].present? - - params[:cycle_analytics].permit(:start_date) - end - def cycle_analytics_json { summary: @cycle_analytics.summary, diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index 514b03e23b5..f13fb4d0b3d 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -73,6 +73,10 @@ class Projects::DeployKeysController < Projects::ApplicationController @deploy_key ||= DeployKey.find(params[:id]) end + def deploy_keys_project + @deploy_keys_project ||= deploy_key.deploy_keys_project_for(@project) + end + def create_params create_params = params.require(:deploy_key) .permit(:key, :title, deploy_keys_projects_attributes: [:can_push]) @@ -81,10 +85,16 @@ class Projects::DeployKeysController < Projects::ApplicationController end def update_params - params.require(:deploy_key).permit(:title, deploy_keys_projects_attributes: [:id, :can_push]) + permitted_params = [deploy_keys_projects_attributes: [:id, :can_push]] + permitted_params << :title if can?(current_user, :update_deploy_key, deploy_key) + + params.require(:deploy_key).permit(*permitted_params) end def authorize_update_deploy_key! - access_denied! unless can?(current_user, :update_deploy_key, deploy_key) + if !can?(current_user, :update_deploy_key, deploy_key) && + !can?(current_user, :update_deploy_keys_project, deploy_keys_project) + access_denied! + end end end diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb index 32111b07a0b..766e2f86ea2 100644 --- a/app/controllers/projects/deployments_controller.rb +++ b/app/controllers/projects/deployments_controller.rb @@ -47,11 +47,9 @@ class Projects::DeploymentsController < Projects::ApplicationController @deployment_metrics ||= DeploymentMetrics.new(deployment.project, deployment) end - # rubocop: disable CodeReuse/ActiveRecord def deployment - @deployment ||= environment.deployments.find_by(iid: params[:id]) + @deployment ||= environment.deployments.find_successful_deployment!(params[:id]) end - # rubocop: enable CodeReuse/ActiveRecord def environment @environment ||= project.environments.find(params[:environment_id]) diff --git a/app/controllers/projects/environments/prometheus_api_controller.rb b/app/controllers/projects/environments/prometheus_api_controller.rb index 9c6c6513a78..e902d218c75 100644 --- a/app/controllers/projects/environments/prometheus_api_controller.rb +++ b/app/controllers/projects/environments/prometheus_api_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Projects::Environments::PrometheusApiController < Projects::ApplicationController + include RenderServiceResults + before_action :authorize_read_prometheus! before_action :environment @@ -12,21 +14,10 @@ class Projects::Environments::PrometheusApiController < Projects::ApplicationCon proxy_params ).execute - if result.nil? - return render status: :no_content, json: { - status: _('processing'), - message: _('Not ready yet. Try again later.') - } - end - - if result[:status] == :success - render status: result[:http_status], json: result[:body] - else - render( - status: result[:http_status] || :bad_request, - json: { status: result[:status], message: result[:message] } - ) - end + return continue_polling_response if result.nil? + return error_response(result) if result[:status] == :error + + success_response(result) end private diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 64de0e665d3..c053ca19a94 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Projects::EnvironmentsController < Projects::ApplicationController + include MetricsDashboard + layout 'project' before_action :authorize_read_environment! before_action :authorize_create_environment!, only: [:new, :create] @@ -12,7 +14,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController before_action :expire_etag_cache, only: [:index] before_action only: [:metrics, :additional_metrics, :metrics_dashboard] do push_frontend_feature_flag(:environment_metrics_use_prometheus_endpoint, default_enabled: true) - push_frontend_feature_flag(:environment_metrics_show_multiple_dashboards) push_frontend_feature_flag(:environment_metrics_additional_panel_types) push_frontend_feature_flag(:prometheus_computed_alerts) end @@ -159,44 +160,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController end end - def metrics_dashboard - if params[:embedded] - result = dashboard_finder.find( - project, - current_user, - environment: environment, - dashboard_path: params[:dashboard], - **dashboard_params.to_h.symbolize_keys - ) - elsif Feature.enabled?(:environment_metrics_show_multiple_dashboards, project) - result = dashboard_finder.find( - project, - current_user, - environment: environment, - dashboard_path: params[:dashboard] - ) - - result[:all_dashboards] = dashboard_finder.find_all_paths(project) - else - result = dashboard_finder.find(project, current_user, environment: environment) - end - - respond_to do |format| - if result[:status] == :success - format.json do - render status: :ok, json: result.slice(:all_dashboards, :dashboard, :status) - end - else - format.json do - render( - status: result[:http_status], - json: result.slice(:all_dashboards, :message, :status) - ) - end - end - end - end - def search respond_to do |format| format.json do @@ -234,12 +197,15 @@ class Projects::EnvironmentsController < Projects::ApplicationController params.require([:start, :end]) end - def dashboard_params - params.permit(:embedded, :group, :title, :y_label) + def metrics_dashboard_params + params + .permit(:embedded, :group, :title, :y_label) + .to_h.symbolize_keys + .merge(dashboard_path: params[:dashboard], environment: environment) end - def dashboard_finder - Gitlab::Metrics::Dashboard::Finder + def include_all_dashboards? + !params[:embedded] end def search_environment_names diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb index a597cc9af32..ccfc38d97b2 100644 --- a/app/controllers/projects/git_http_client_controller.rb +++ b/app/controllers/projects/git_http_client_controller.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -# This file should be identical in GitLab Community Edition and Enterprise Edition - class Projects::GitHttpClientController < Projects::ApplicationController include ActionController::HttpAuthentication::Basic include KerberosSpnegoHelper diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb index 0c8c03cb16a..93f7ce73a51 100644 --- a/app/controllers/projects/git_http_controller.rb +++ b/app/controllers/projects/git_http_controller.rb @@ -6,10 +6,10 @@ class Projects::GitHttpController < Projects::GitHttpClientController before_action :access_check prepend_before_action :deny_head_requests, only: [:info_refs] - rescue_from Gitlab::GitAccess::UnauthorizedError, with: :render_403 - rescue_from Gitlab::GitAccess::NotFoundError, with: :render_404 - rescue_from Gitlab::GitAccess::ProjectCreationError, with: :render_422 - rescue_from Gitlab::GitAccess::TimeoutError, with: :render_503 + rescue_from Gitlab::GitAccess::UnauthorizedError, with: :render_403_with_exception + rescue_from Gitlab::GitAccess::NotFoundError, with: :render_404_with_exception + rescue_from Gitlab::GitAccess::ProjectCreationError, with: :render_422_with_exception + rescue_from Gitlab::GitAccess::TimeoutError, with: :render_503_with_exception # GET /foo/bar.git/info/refs?service=git-upload-pack (git pull) # GET /foo/bar.git/info/refs?service=git-receive-pack (git push) @@ -58,19 +58,19 @@ class Projects::GitHttpController < Projects::GitHttpClientController render json: Gitlab::Workhorse.git_http_ok(repository, repo_type, user, action_name) end - def render_403(exception) + def render_403_with_exception(exception) render plain: exception.message, status: :forbidden end - def render_404(exception) + def render_404_with_exception(exception) render plain: exception.message, status: :not_found end - def render_422(exception) + def render_422_with_exception(exception) render plain: exception.message, status: :unprocessable_entity end - def render_503(exception) + def render_503_with_exception(exception) render plain: exception.message, status: :service_unavailable end diff --git a/app/controllers/projects/grafana_api_controller.rb b/app/controllers/projects/grafana_api_controller.rb new file mode 100644 index 00000000000..4bdf4c12cac --- /dev/null +++ b/app/controllers/projects/grafana_api_controller.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class Projects::GrafanaApiController < Projects::ApplicationController + include RenderServiceResults + + def proxy + result = ::Grafana::ProxyService.new( + project, + params[:datasource_id], + params[:proxy_path], + query_params.to_h + ).execute + + return continue_polling_response if result.nil? + return error_response(result) if result[:status] == :error + + success_response(result) + end + + private + + def query_params + params.permit(:query, :start, :end, :step) + end +end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 7a192a9ec2d..96cb400950b 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -42,6 +42,10 @@ class Projects::IssuesController < Projects::ApplicationController before_action :authorize_import_issues!, only: [:import_csv] before_action :authorize_download_code!, only: [:related_branches] + before_action do + push_frontend_feature_flag(:vue_issuable_sidebar, project.group) + end + respond_to :html alias_method :designs, :show diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index 0fdd4d4f33d..1d914ab6011 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -11,8 +11,8 @@ class Projects::JobsController < Projects::ApplicationController before_action :authorize_erase_build!, only: [:erase] before_action :authorize_use_build_terminal!, only: [:terminal, :terminal_websocket_authorize] before_action :verify_api_request!, only: :terminal_websocket_authorize - before_action only: [:trace] do - push_frontend_feature_flag(:job_log_json) + before_action only: [:show] do + push_frontend_feature_flag(:job_log_json, project) end layout 'project' @@ -67,38 +67,27 @@ class Projects::JobsController < Projects::ApplicationController # rubocop: enable CodeReuse/ActiveRecord def trace - if Feature.enabled?(:job_log_json, @project) - json_trace - else - html_trace - end - end - - def html_trace build.trace.read do |stream| respond_to do |format| format.json do - result = { - id: @build.id, status: @build.status, complete: @build.complete? - } - - if stream.valid? - stream.limit - state = params[:state].presence - trace = stream.html_with_state(state) - result.merge!(trace.to_h) - end - - render json: result + # TODO: when the feature flag is removed we should not pass + # content_format to serialize method. + content_format = Feature.enabled?(:job_log_json, @project) ? :json : :html + + build_trace = Ci::BuildTrace.new( + build: @build, + stream: stream, + state: params[:state], + content_format: content_format) + + render json: BuildTraceSerializer + .new(project: @project, current_user: @current_user) + .represent(build_trace) end end end end - def json_trace - # will be implemented with https://gitlab.com/gitlab-org/gitlab-foss/issues/66454 - end - def retry return respond_422 unless @build.retryable? diff --git a/app/controllers/projects/lfs_api_controller.rb b/app/controllers/projects/lfs_api_controller.rb index 739f7a2437e..a1983bc5462 100644 --- a/app/controllers/projects/lfs_api_controller.rb +++ b/app/controllers/projects/lfs_api_controller.rb @@ -2,6 +2,7 @@ class Projects::LfsApiController < Projects::GitHttpClientController include LfsRequest + include Gitlab::Utils::StrongMemoize LFS_TRANSFER_CONTENT_TYPE = 'application/octet-stream' @@ -81,7 +82,7 @@ class Projects::LfsApiController < Projects::GitHttpClientController download: { href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}", header: { - Authorization: request.headers['Authorization'] + Authorization: authorization_header }.compact } } @@ -92,7 +93,7 @@ class Projects::LfsApiController < Projects::GitHttpClientController upload: { href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}/#{object[:size]}", header: { - Authorization: request.headers['Authorization'], + Authorization: authorization_header, # git-lfs v2.5.0 sets the Content-Type based on the uploaded file. This # ensures that Workhorse can intercept the request. 'Content-Type': LFS_TRANSFER_CONTENT_TYPE @@ -122,6 +123,18 @@ class Projects::LfsApiController < Projects::GitHttpClientController def lfs_read_only_message _('You cannot write to this read-only GitLab instance.') end + + def authorization_header + strong_memoize(:authorization_header) do + lfs_auth_header || request.headers['Authorization'] + end + end + + def lfs_auth_header + return unless user.is_a?(User) + + Gitlab::LfsToken.new(user).basic_encoding + end end Projects::LfsApiController.prepend_if_ee('EE::Projects::LfsApiController') diff --git a/app/controllers/projects/merge_requests/application_controller.rb b/app/controllers/projects/merge_requests/application_controller.rb index edffeb32203..b7e99cb7ed0 100644 --- a/app/controllers/projects/merge_requests/application_controller.rb +++ b/app/controllers/projects/merge_requests/application_controller.rb @@ -14,7 +14,11 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont end def merge_request_includes(association) - association.includes(:metrics, :assignees, author: :status) # rubocop:disable CodeReuse/ActiveRecord + association.includes(preloadable_mr_relations) # rubocop:disable CodeReuse/ActiveRecord + end + + def preloadable_mr_relations + [:metrics, :assignees, { author: :status }] end def merge_request_params diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index 9c5caf7719e..4a37dfe5c19 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -5,9 +5,9 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic include RendersNotes before_action :apply_diff_view_cookie! - before_action :commit - before_action :define_diff_vars - before_action :define_diff_comment_vars + before_action :commit, except: :diffs_batch + before_action :define_diff_vars, except: :diffs_batch + before_action :define_diff_comment_vars, except: [:diffs_batch, :diffs_metadata] def show render_diffs @@ -17,14 +17,41 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic render_diffs end + def diffs_batch + return render_404 unless Feature.enabled?(:diffs_batch_load, @merge_request.project) + + diffable = @merge_request.merge_request_diff + + return render_404 unless diffable + + diffs = diffable.diffs_in_batch(params[:page], params[:per_page], diff_options: diff_options) + positions = @merge_request.note_positions_for_paths(diffs.diff_file_paths, current_user) + + diffs.unfold_diff_files(positions.unfoldable) + + options = { + merge_request: @merge_request, + pagination_data: diffs.pagination_data + } + + render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options) + end + + def diffs_metadata + render json: DiffsMetadataSerializer.new(project: @merge_request.project) + .represent(@diffs, additional_attributes) + end + private + def preloadable_mr_relations + [{ source_project: :namespace }, { target_project: :namespace }] + end + def render_diffs @environment = @merge_request.environments_for(current_user).last - note_positions = renderable_notes.map(&:position).compact - @diffs.unfold_diff_files(note_positions) - + @diffs.unfold_diff_files(note_positions.unfoldable) @diffs.write_cache request = { @@ -111,6 +138,10 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic @notes = prepare_notes_for_rendering(@grouped_diff_discussions.values.flatten.flat_map(&:notes), @merge_request) end + def note_positions + @note_positions ||= Gitlab::Diff::PositionCollection.new(renderable_notes.map(&:position)) + end + def renderable_notes define_diff_comment_vars unless @notes diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index e51ce752233..ff199e05e99 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -5,6 +5,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo include IssuableActions include RendersNotes include RendersCommits + include RendersAssignees include ToggleAwardEmoji include IssuableCollections include RecordUserLastActivity @@ -16,6 +17,13 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo before_action :set_issuables_index, only: [:index] before_action :authenticate_user!, only: [:assign_related_issues] before_action :check_user_can_push_to_source_branch!, only: [:rebase] + before_action only: [:show] do + push_frontend_feature_flag(:diffs_batch_load, @project) + end + + before_action do + push_frontend_feature_flag(:vue_issuable_sidebar, @project.group) + end around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :discussions] @@ -41,6 +49,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo # use next to appease Rubocop next render('invalid') if target_branch_missing? + preload_assignees_for_render(@merge_request) + # Build a note object for comment form @note = @project.notes.new(noteable: @merge_request) @@ -79,7 +89,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo # Get commits from repository # or from cache if already merged @commits = - set_commits_for_rendering(@merge_request.commits.with_pipeline_status) + set_commits_for_rendering(@merge_request.commits.with_latest_pipeline) render json: { html: view_to_html_string('projects/merge_requests/_commits') } end diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index cfa46705483..106ef1b72c1 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Projects::PipelinesController < Projects::ApplicationController + include ::Gitlab::Utils::StrongMemoize + before_action :whitelist_query_limiting, only: [:create, :retry] before_action :pipeline, except: [:index, :new, :create, :charts] before_action :set_pipeline_path, only: [:show] @@ -151,6 +153,19 @@ class Projects::PipelinesController < Projects::ApplicationController @counts[:failed] = @project.all_pipelines.failed.count(:all) end + def test_report + return unless Feature.enabled?(:junit_pipeline_view, project) + + if pipeline_test_report == :error + render json: { status: :error_parsing_report } + return + end + + render json: TestReportSerializer + .new(current_user: @current_user) + .represent(pipeline_test_report) + end + private def serialize_pipelines @@ -169,7 +184,7 @@ class Projects::PipelinesController < Projects::ApplicationController end def show_represent_params - { grouped: true } + { grouped: true, expanded: params[:expanded].to_a.map(&:to_i) } end def create_params @@ -217,6 +232,14 @@ class Projects::PipelinesController < Projects::ApplicationController view_context.limited_counter_with_delimiter(finder.execute) end + + def pipeline_test_report + strong_memoize(:pipeline_test_report) do + @pipeline.test_reports + rescue Gitlab::Ci::Parsers::ParserError + :error + end + end end Projects::PipelinesController.prepend_if_ee('EE::Projects::PipelinesController') diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb index c5454883060..d4f7d0bc521 100644 --- a/app/controllers/projects/protected_branches_controller.rb +++ b/app/controllers/projects/protected_branches_controller.rb @@ -19,9 +19,13 @@ class Projects::ProtectedBranchesController < Projects::ProtectedRefsController [:merge_access_levels, :push_access_levels] end - def protected_ref_params - params.require(:protected_branch).permit(:name, - merge_access_levels_attributes: access_level_attributes, - push_access_levels_attributes: access_level_attributes) + def protected_ref_params(*attrs) + attrs = ([:name, + merge_access_levels_attributes: access_level_attributes, + push_access_levels_attributes: access_level_attributes] + attrs).uniq + + params.require(:protected_branch).permit(attrs) end end + +Projects::ProtectedBranchesController.prepend_if_ee('EE::Projects::ProtectedBranchesController') diff --git a/app/controllers/projects/registry/repositories_controller.rb b/app/controllers/projects/registry/repositories_controller.rb index e205e2fd4f8..9405fd526ae 100644 --- a/app/controllers/projects/registry/repositories_controller.rb +++ b/app/controllers/projects/registry/repositories_controller.rb @@ -8,6 +8,7 @@ module Projects def index @images = project.container_repositories + track_event(:list_repositories) respond_to do |format| format.html @@ -21,6 +22,7 @@ module Projects def destroy DeleteContainerRepositoryWorker.perform_async(current_user.id, image.id) + track_event(:delete_repository) respond_to do |format| format.json { head :no_content } diff --git a/app/controllers/projects/registry/tags_controller.rb b/app/controllers/projects/registry/tags_controller.rb index 54e2faa2dd7..e572c56adf5 100644 --- a/app/controllers/projects/registry/tags_controller.rb +++ b/app/controllers/projects/registry/tags_controller.rb @@ -8,6 +8,7 @@ module Projects LIMIT = 15 def index + track_event(:list_tags) respond_to do |format| format.json do render json: ContainerTagsSerializer @@ -19,14 +20,13 @@ module Projects end def destroy - if tag.delete - respond_to do |format| - format.json { head :no_content } - end - else - respond_to do |format| - format.json { head :bad_request } - end + result = Projects::ContainerRepository::DeleteTagsService + .new(image.project, current_user, tags: [params[:id]]) + .execute(image) + track_event(:delete_tag) + + respond_to do |format| + format.json { head(result[:status] == :success ? :ok : bad_request) } end end @@ -42,21 +42,13 @@ module Projects return end - @tags = tag_names.map { |tag_name| image.tag(tag_name) } - unless @tags.all? { |tag| tag.valid_name? } - head :bad_request - return - end - - success_count = 0 - @tags.each do |tag| - if tag.delete - success_count += 1 - end - end + result = Projects::ContainerRepository::DeleteTagsService + .new(image.project, current_user, tags: tag_names) + .execute(image) + track_event(:delete_tag_bulk) respond_to do |format| - format.json { head(success_count == @tags.size ? :no_content : :bad_request) } + format.json { head(result[:status] == :success ? :no_content : :bad_request) } end end @@ -70,10 +62,6 @@ module Projects @image ||= project.container_repositories .find(params[:repository_id]) end - - def tag - @tag ||= image.tag(params[:id]) - end end end end diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb index 4c39ee4045f..717df9f09e0 100644 --- a/app/controllers/projects/releases_controller.rb +++ b/app/controllers/projects/releases_controller.rb @@ -4,6 +4,9 @@ class Projects::ReleasesController < Projects::ApplicationController # Authorize before_action :require_non_empty_project before_action :authorize_read_release! + before_action do + push_frontend_feature_flag(:release_edit_page, project) + end def index end diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index 0d61c3cc031..cfed8727450 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -46,13 +46,19 @@ module Projects private def update_params - params.require(:project).permit( + params.require(:project).permit(*permitted_project_params) + end + + def permitted_project_params + [ :runners_token, :builds_enabled, :build_allow_git_fetch, :build_timeout_human_readable, :build_coverage_regex, :public_builds, :auto_cancel_pending_pipelines, :ci_config_path, auto_devops_attributes: [:id, :domain, :enabled, :deploy_strategy], ci_cd_settings_attributes: [:default_git_depth] - ) + ].tap do |list| + list << :max_artifacts_size if can?(current_user, :update_max_artifacts_size, project) + end end def run_autodevops_pipeline(service) diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb index 7c71486a765..5bf3618b389 100644 --- a/app/controllers/projects/settings/operations_controller.rb +++ b/app/controllers/projects/settings/operations_controller.rb @@ -13,9 +13,14 @@ module Projects def update result = ::Projects::Operations::UpdateService.new(project, current_user, update_params).execute + track_events(result) render_update_response(result) end + # overridden in EE + def track_events(result) + end + private # overridden in EE @@ -63,7 +68,9 @@ module Projects :api_host, :token, project: [:slug, :name, :organization_slug, :organization_name] - ] + ], + + grafana_integration_attributes: [:token, :grafana_url] } end end diff --git a/app/controllers/projects/templates_controller.rb b/app/controllers/projects/templates_controller.rb index f987033a26c..95739f96d39 100644 --- a/app/controllers/projects/templates_controller.rb +++ b/app/controllers/projects/templates_controller.rb @@ -13,6 +13,14 @@ class Projects::TemplatesController < Projects::ApplicationController end end + def names + templates = @template_type.dropdown_names(project) + + respond_to do |format| + format.json { render json: templates } + end + end + private # User must have: diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb index 4ffcc2ac805..3e5a1cfc74d 100644 --- a/app/controllers/projects/uploads_controller.rb +++ b/app/controllers/projects/uploads_controller.rb @@ -6,7 +6,7 @@ class Projects::UploadsController < Projects::ApplicationController # These will kick you out if you don't have access. skip_before_action :project, :repository, - if: -> { action_name == 'show' && image_or_video? } + if: -> { action_name == 'show' && embeddable? } before_action :authorize_upload_file!, only: [:create, :authorize] before_action :verify_workhorse_api!, only: [:authorize] diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index b8beecf823c..abd19df9a3d 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -376,6 +376,7 @@ class ProjectsController < Projects::ApplicationController :tag_list, :visibility_level, :template_name, + :template_project_id, :merge_method, :initialize_with_readme, diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 2e4c6a801b0..4a746fc915d 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -6,13 +6,20 @@ class RegistrationsController < Devise::RegistrationsController include RecaptchaExperimentHelper include InvisibleCaptcha + layout :choose_layout + + skip_before_action :require_role, only: [:welcome, :update_role] prepend_before_action :check_captcha, only: :create before_action :whitelist_query_limiting, only: [:destroy] before_action :ensure_terms_accepted, if: -> { action_name == 'create' && Gitlab::CurrentSettings.current_application_settings.enforce_terms? } def new - redirect_to(new_user_session_path) + if experiment_enabled?(:signup_flow) + @resource = build_resource + else + redirect_to new_user_session_path(anchor: 'register-pane') + end end def create @@ -20,8 +27,13 @@ class RegistrationsController < Devise::RegistrationsController super do |new_user| persist_accepted_terms_if_required(new_user) + set_role_required(new_user) yield new_user if block_given? end + + # Do not show the signed_up notice message when the signup_flow experiment is enabled. + # Instead, show it after succesfully updating the role. + flash[:notice] = nil if experiment_enabled?(:signup_flow) rescue Gitlab::Access::AccessDeniedError redirect_to(new_user_session_path) end @@ -36,6 +48,26 @@ class RegistrationsController < Devise::RegistrationsController end end + def welcome + return redirect_to new_user_registration_path unless current_user + return redirect_to stored_location_or_dashboard_or_almost_there_path(current_user) if current_user.role.present? + + current_user.name = nil + render layout: 'devise_experimental_separate_sign_up_flow' + end + + def update_role + user_params = params.require(:user).permit(:name, :role) + result = ::Users::UpdateService.new(current_user, user_params.merge(user: current_user)).execute + + if result[:status] == :success + set_flash_message! :notice, :signed_up + redirect_to stored_location_or_dashboard_or_almost_there_path(current_user) + else + redirect_to users_sign_up_welcome_path, alert: result[:message] + end + end + protected def persist_accepted_terms_if_required(new_user) @@ -48,6 +80,10 @@ class RegistrationsController < Devise::RegistrationsController end end + def set_role_required(new_user) + new_user.set_role_required! if new_user.persisted? && experiment_enabled?(:signup_flow) + end + def destroy_confirmation_valid? if current_user.confirm_deletion_with_password? current_user.valid_password?(params[:password]) @@ -70,7 +106,10 @@ class RegistrationsController < Devise::RegistrationsController def after_sign_up_path_for(user) Gitlab::AppLogger.info(user_created_message(confirmed: user.confirmed?)) - confirmed_or_unconfirmed_access_allowed(user) ? stored_location_or_dashboard(user) : users_almost_there_path + + return users_sign_up_welcome_path if experiment_enabled?(:signup_flow) + + stored_location_or_dashboard_or_almost_there_path(user) end def after_inactive_sign_up_path_for(resource) @@ -97,6 +136,7 @@ class RegistrationsController < Devise::RegistrationsController ensure_correct_params! return unless Feature.enabled?(:registrations_recaptcha, default_enabled: true) # reCAPTCHA on the UI will still display however + return if experiment_enabled?(:signup_flow) # when the experimental signup flow is enabled for the current user, disable the reCAPTCHA check return unless show_recaptcha_sign_up? return unless Gitlab::Recaptcha.load_configurations! @@ -108,7 +148,13 @@ class RegistrationsController < Devise::RegistrationsController end def sign_up_params - params.require(:user).permit(:username, :email, :email_confirmation, :name, :password) + clean_params = params.require(:user).permit(:username, :email, :email_confirmation, :name, :password) + + if experiment_enabled?(:signup_flow) + clean_params[:name] = clean_params[:username] + end + + clean_params end def resource_name @@ -138,12 +184,26 @@ class RegistrationsController < Devise::RegistrationsController end def confirmed_or_unconfirmed_access_allowed(user) - user.confirmed? || Feature.enabled?(:soft_email_confirmation) + user.confirmed? || Feature.enabled?(:soft_email_confirmation) || experiment_enabled?(:signup_flow) end def stored_location_or_dashboard(user) stored_location_for(user) || dashboard_projects_path end + + def stored_location_or_dashboard_or_almost_there_path(user) + confirmed_or_unconfirmed_access_allowed(user) ? stored_location_or_dashboard(user) : users_almost_there_path + end + + # Part of an experiment to build a new sign up flow. Will be resolved + # with https://gitlab.com/gitlab-org/growth/engineering/issues/64 + def choose_layout + if experiment_enabled?(:signup_flow) + 'devise_experimental_separate_sign_up_flow' + else + 'devise' + end + end end RegistrationsController.prepend_if_ee('EE::RegistrationsController') diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index f8da152e3d2..1c506065b56 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -57,8 +57,14 @@ class SessionsController < Devise::SessionsController reset_password_sent_at: nil) end - # hide the signed-in notification - flash[:notice] = nil + if resource.deactivated? + resource.activate + flash[:notice] = _('Welcome back! Your account had been deactivated due to inactivity but is now reactivated.') + else + # hide the default signed-in notification + flash[:notice] = nil + end + log_audit_event(current_user, resource, with: authentication_method) log_user_activity(current_user) end diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index 2adfeab182e..635db386792 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -81,8 +81,13 @@ class UploadsController < ApplicationController end end - def cache_publicly? - User === model || Appearance === model + def cache_settings + case model + when User, Appearance + [5.minutes, { public: true, must_revalidate: false }] + when Project, Group + [5.minutes, { private: true, must_revalidate: true }] + end end def secret? -- cgit v1.2.3