diff options
Diffstat (limited to 'app/controllers')
64 files changed, 577 insertions, 786 deletions
diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb index 251ba9e29f2..edd85414696 100644 --- a/app/controllers/admin/broadcast_messages_controller.rb +++ b/app/controllers/admin/broadcast_messages_controller.rb @@ -10,6 +10,9 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController # rubocop: disable CodeReuse/ActiveRecord def index + push_frontend_feature_flag(:vue_broadcast_messages, current_user) + push_frontend_feature_flag(:role_targeted_broadcast_messages, current_user) + @broadcast_messages = BroadcastMessage.order(ends_at: :desc).page(params[:page]) @broadcast_message = BroadcastMessage.new end diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 2ae0442c005..f3c4244269d 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -60,17 +60,6 @@ class Admin::GroupsController < Admin::ApplicationController end end - def members_update - member_params = params.permit(:user_id, :access_level, :expires_at) - result = Members::CreateService.new(current_user, member_params.merge(limit: -1, source: @group, invite_source: 'admin-group-page')).execute - - if result[:status] == :success - redirect_to [:admin, @group], notice: _('Users were successfully added.') - else - redirect_to [:admin, @group], alert: result[:message] - end - end - def destroy Groups::DestroyService.new(@group, current_user).async_execute diff --git a/app/controllers/admin/impersonation_tokens_controller.rb b/app/controllers/admin/impersonation_tokens_controller.rb index eb279298baf..9d884478e98 100644 --- a/app/controllers/admin/impersonation_tokens_controller.rb +++ b/app/controllers/admin/impersonation_tokens_controller.rb @@ -14,11 +14,10 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController @impersonation_token = finder.build(impersonation_token_params) if @impersonation_token.save - PersonalAccessToken.redis_store!(current_user.id, @impersonation_token.token) - redirect_to admin_user_impersonation_tokens_path, notice: _("A new impersonation token has been created.") + render json: { new_token: @impersonation_token.token, + active_access_tokens: active_impersonation_tokens }, status: :ok else - set_index_vars - render :index + render json: { errors: @impersonation_token.errors.full_messages }, status: :unprocessable_entity end end @@ -50,19 +49,19 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController PersonalAccessTokensFinder.new({ user: user, impersonation: true }.merge(options)) end + def active_impersonation_tokens + tokens = finder(state: 'active', sort: 'expires_at_asc_id_desc').execute + ::ImpersonationAccessTokenSerializer.new.represent(tokens) + end + def impersonation_token_params params.require(:personal_access_token).permit(:name, :expires_at, :impersonation, scopes: []) end - # rubocop: disable CodeReuse/ActiveRecord def set_index_vars @scopes = Gitlab::Auth.available_scopes_for(current_user) @impersonation_token ||= finder.build - @inactive_impersonation_tokens = finder(state: 'inactive').execute - @active_impersonation_tokens = finder(state: 'active').execute.order(:expires_at) - - @new_impersonation_token = PersonalAccessToken.redis_getdel(current_user.id) + @active_impersonation_tokens = active_impersonation_tokens end - # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb index a0f72f5e58c..96fe0c9331d 100644 --- a/app/controllers/admin/runners_controller.rb +++ b/app/controllers/admin/runners_controller.rb @@ -4,13 +4,6 @@ class Admin::RunnersController < Admin::ApplicationController include RunnerSetupScripts before_action :runner, except: [:index, :tag_list, :runner_setup_scripts] - before_action only: [:index] do - push_frontend_feature_flag(: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 diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 71d9910b4b8..84efb8b0da8 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -16,7 +16,6 @@ class ApplicationController < ActionController::Base include SessionlessAuthentication include SessionsHelper include ConfirmEmailWarning - include Gitlab::Experimentation::ControllerConcern include InitializesCurrentUserMode include Impersonation include Gitlab::Logging::CloudflareHelper diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 88592efcec7..45585ab84b4 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -25,7 +25,20 @@ class AutocompleteController < ApplicationController .new(params: params, current_user: current_user, project: project, group: group) .execute - render json: UserSerializer.new(params.merge({ current_user: current_user })).represent(users, project: project) + presented_users = UserSerializer + .new(params.merge({ current_user: current_user })) + .represent(users, project: project) + + extra_users = presented_suggested_users + + if extra_users.present? + presented_users.reject! do |user| + extra_users.any? { |suggested_user| suggested_user[:id] == user[:id] } + end + presented_users += extra_users + end + + render json: presented_users end def user @@ -80,6 +93,11 @@ class AutocompleteController < ApplicationController def target_branch_params params.permit(:group_id, :project_id).select { |_, v| v.present? } end + + # overridden in EE + def presented_suggested_users + [] + end end AutocompleteController.prepend_mod_with('AutocompleteController') diff --git a/app/controllers/boards/application_controller.rb b/app/controllers/boards/application_controller.rb deleted file mode 100644 index 15ef6698472..00000000000 --- a/app/controllers/boards/application_controller.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module Boards - class ApplicationController < ::ApplicationController - respond_to :json - - rescue_from ActiveRecord::RecordNotFound, with: :record_not_found - - private - - def board - @board ||= Board.find(params[:board_id]) - end - - def board_parent - @board_parent ||= board.resource_parent - end - - def record_not_found(exception) - render json: { error: exception.message }, status: :not_found - end - end -end diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb deleted file mode 100644 index 5028544795c..00000000000 --- a/app/controllers/boards/issues_controller.rb +++ /dev/null @@ -1,162 +0,0 @@ -# frozen_string_literal: true - -module Boards - class IssuesController < Boards::ApplicationController - # This is the maximum amount of issues which can be moved by one request to - # bulk_move for now. This is temporary and might be removed in future by - # introducing an alternative (async?) approach. - # (related: https://gitlab.com/groups/gitlab-org/-/epics/382) - MAX_MOVE_ISSUES_COUNT = 50 - - include BoardsResponses - include ControllerWithCrossProjectAccessCheck - - requires_cross_project_access if: -> { board&.group_board? } - - before_action :disable_query_limiting, only: [:bulk_move] - before_action :authorize_read_issue, only: [:index] - before_action :authorize_create_issue, only: [:create] - before_action :authorize_update_issue, only: [:update] - skip_before_action :authenticate_user!, only: [:index] - before_action :validate_id_list, only: [:bulk_move] - before_action :can_move_issues?, only: [:bulk_move] - - feature_category :team_planning - urgency :low - - def index - list_service = Boards::Issues::ListService.new(board_parent, current_user, filter_params) - issues = issues_from(list_service) - - ::Boards::Issues::ListService.initialize_relative_positions(board, current_user, issues) - - render_issues(issues, list_service.metadata) - end - - def create - service = Boards::Issues::CreateService.new(board_parent, project, current_user, issue_params) - issue = service.execute - - if issue.valid? - render json: serialize_as_json(issue) - else - render json: issue.errors, status: :unprocessable_entity - end - end - - def bulk_move - service = Boards::Issues::MoveService.new(board_parent, current_user, move_params(true)) - - issues = Issue.find(params[:ids]) - - render json: service.execute_multiple(issues) - end - - def update - service = Boards::Issues::MoveService.new(board_parent, current_user, move_params) - - if service.execute(issue) - head :ok - else - head :unprocessable_entity - end - end - - private - - def issues_from(list_service) - issues = list_service.execute - issues.page(params[:page]).per(params[:per] || 20) - .without_count - .preload(associations_to_preload) # rubocop: disable CodeReuse/ActiveRecord - .load - end - - def associations_to_preload - [ - :milestone, - :assignees, - project: [ - :route, - { - namespace: [:route] - } - ], - labels: [:priorities], - notes: [:award_emoji, :author] - ] - end - - def can_move_issues? - head(:forbidden) unless can?(current_user, :admin_issue, board) - end - - def serializer_options(issues) - {} - end - - def render_issues(issues, metadata) - data = { issues: serialize_as_json(issues, opts: serializer_options(issues)) } - data.merge!(metadata) - - render json: data - end - - def issue - @issue ||= issues_finder.find(params[:id]) - end - - def filter_params - params.permit(*Boards::Issues::ListService.valid_params).merge(board_id: params[:board_id], id: params[:list_id]) - .reject { |_, value| value.nil? } - end - - def issues_finder - if board.group_board? - IssuesFinder.new(current_user, group_id: board_parent.id) - else - IssuesFinder.new(current_user, project_id: board_parent.id) - end - end - - def project - @project ||= if board.group_board? - Project.find(issue_params[:project_id]) - else - board_parent - end - end - - def move_params(multiple = false) - id_param = multiple ? :ids : :id - params.permit(id_param, :board_id, :from_list_id, :to_list_id, :move_before_id, :move_after_id) - end - - def issue_params - params.require(:issue) - .permit(:title, :milestone_id, :project_id) - .merge(board_id: params[:board_id], list_id: params[:list_id]) - end - - def serializer - IssueSerializer.new(current_user: current_user) - end - - def serialize_as_json(resource, opts: {}) - opts.merge!(include_full_project_path: board.group_board?, serializer: 'board') - - serializer.represent(resource, opts) - end - - def disable_query_limiting - Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/issues/35174') - end - - def validate_id_list - head(:bad_request) unless params[:ids].is_a?(Array) - head(:unprocessable_entity) if params[:ids].size > MAX_MOVE_ISSUES_COUNT - end - end -end - -Boards::IssuesController.prepend_mod_with('Boards::IssuesController') diff --git a/app/controllers/boards/lists_controller.rb b/app/controllers/boards/lists_controller.rb deleted file mode 100644 index c3b5a887920..00000000000 --- a/app/controllers/boards/lists_controller.rb +++ /dev/null @@ -1,103 +0,0 @@ -# frozen_string_literal: true - -module Boards - class ListsController < Boards::ApplicationController - include BoardsResponses - - before_action :authorize_admin_list, only: [:create, :destroy, :generate] - before_action :authorize_read_list, only: [:index] - skip_before_action :authenticate_user!, only: [:index] - - feature_category :team_planning - urgency :low - - def index - 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 - response = Boards::Lists::CreateService.new(board.resource_parent, current_user, create_list_params).execute(board) - - if response.success? - render json: serialize_as_json(response.payload[:list]) - else - render json: { errors: response.errors }, status: :unprocessable_entity - end - end - - def update - list = board.lists.find(params[:id]) - service = Boards::Lists::UpdateService.new(board_parent, current_user, update_list_params) - result = service.execute(list) - - if result.success? - head :ok - else - head result.http_status - end - end - - def destroy - list = board.lists.destroyable.find(params[:id]) - service = Boards::Lists::DestroyService.new(board_parent, current_user) - - if service.execute(list).success? - head :ok - else - head :unprocessable_entity - end - end - - def generate - service = Boards::Lists::GenerateService.new(board_parent, current_user) - - if service.execute(board) - lists = board.lists.movable.preload_associated_models - - List.preload_preferences_for_user(lists, current_user) - - render json: serialize_as_json(lists) - else - head :unprocessable_entity - end - end - - private - - def list_creation_attrs - %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(list_update_attrs) - end - - def serialize_as_json(resource) - resource.as_json(serialization_attrs) - end - - def serialization_attrs - { - only: [:id, :list_type, :position], - methods: [:title], - label: true, - collapsed: true, - current_user: current_user - } - end - end -end - -Boards::ListsController.prepend_mod_with('Boards::ListsController') diff --git a/app/controllers/concerns/access_tokens_actions.rb b/app/controllers/concerns/access_tokens_actions.rb index 451841c43bb..6e43be5594d 100644 --- a/app/controllers/concerns/access_tokens_actions.rb +++ b/app/controllers/concerns/access_tokens_actions.rb @@ -22,11 +22,10 @@ module AccessTokensActions if token_response.success? @resource_access_token = token_response.payload[:access_token] - PersonalAccessToken.redis_store!(key_identity, @resource_access_token.token) - - redirect_to resource_access_tokens_path, notice: _("Your new access token has been created.") + render json: { new_token: @resource_access_token.token, + active_access_tokens: active_resource_access_tokens }, status: :ok else - redirect_to resource_access_tokens_path, alert: _("Failed to create new access token: %{token_response_message}") % { token_response_message: token_response.message } + render json: { errors: token_response.errors }, status: :unprocessable_entity end end # rubocop:enable Gitlab/ModuleWithInstanceVariables @@ -63,12 +62,15 @@ module AccessTokensActions resource.members.load @scopes = Gitlab::Auth.resource_bot_scopes - @active_resource_access_tokens = finder(state: 'active').execute.preload_users - @inactive_resource_access_tokens = finder(state: 'inactive', sort: 'expires_at_asc').execute.preload_users - @new_resource_access_token = PersonalAccessToken.redis_getdel(key_identity) + @active_resource_access_tokens = active_resource_access_tokens end # rubocop:enable Gitlab/ModuleWithInstanceVariables + def active_resource_access_tokens + tokens = finder(state: 'active', sort: 'expires_at_asc_id_desc').execute.preload_users + represent(tokens) + end + def finder(options = {}) PersonalAccessTokensFinder.new({ user: bot_users, impersonation: false }.merge(options)) end diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb index 4228a93d310..fbaa754124c 100644 --- a/app/controllers/concerns/authenticates_with_two_factor.rb +++ b/app/controllers/concerns/authenticates_with_two_factor.rb @@ -89,6 +89,7 @@ module AuthenticatesWithTwoFactor user.save! sign_in(user, message: :two_factor_authenticated, event: :authentication) else + send_two_factor_otp_attempt_failed_email(user) handle_two_factor_failure(user, 'OTP', _('Invalid two-factor code.')) end end @@ -158,6 +159,10 @@ module AuthenticatesWithTwoFactor prompt_for_two_factor(user) end + def send_two_factor_otp_attempt_failed_email(user) + user.notification_service.two_factor_otp_attempt_failed(user, request.remote_ip) + end + def log_failed_two_factor(user, method) # overridden in EE end diff --git a/app/controllers/concerns/boards_actions.rb b/app/controllers/concerns/boards_actions.rb index 2f9edfad12d..42bf6c68aa7 100644 --- a/app/controllers/concerns/boards_actions.rb +++ b/app/controllers/concerns/boards_actions.rb @@ -5,41 +5,38 @@ module BoardsActions extend ActiveSupport::Concern included do - include BoardsResponses - before_action :authorize_read_board!, only: [:index, :show] - before_action :boards, only: :index - before_action :board, only: :show + before_action :redirect_to_recent_board, only: [:index] + before_action :board, only: [:index, :show] before_action :push_licensed_features, only: [:index, :show] end def index - respond_with_boards + # if no board exists, create one + @board = board_create_service.execute.payload unless board # rubocop:disable Gitlab/ModuleWithInstanceVariables end def show - # Add / update the board in the recent visits table - board_visit_service.new(parent, current_user).execute(board) if request.format.html? + return render_404 unless board - respond_with_board + # Add / update the board in the recent visits table + board_visit_service.new(parent, current_user).execute(board) end private - # Noop on FOSS - def push_licensed_features + def redirect_to_recent_board + return if !parent.multiple_issue_boards_available? || !latest_visited_board + + redirect_to board_path(latest_visited_board.board) end - def boards - strong_memoize(:boards) do - existing_boards = boards_finder.execute - if existing_boards.any? - existing_boards - else - # if no board exists, create one - [board_create_service.execute.payload] - end - end + def latest_visited_board + @latest_visited_board ||= Boards::VisitsFinder.new(parent, current_user).latest + end + + # Noop on FOSS + def push_licensed_features end def board @@ -48,20 +45,26 @@ module BoardsActions end end - def board_type - board_klass.to_type - end - def board_visit_service Boards::Visits::CreateService end - def serializer - BoardSerializer.new(current_user: current_user) + def parent + strong_memoize(:parent) do + group? ? group : project + end + end + + def board_path(board) + if group? + group_board_path(parent, board) + else + project_board_path(parent, board) + end end - def serialize_as_json(resource) - serializer.represent(resource, serializer: 'board', include_full_project_path: board.group_board?) + def group? + instance_variable_defined?(:@group) end end diff --git a/app/controllers/concerns/boards_responses.rb b/app/controllers/concerns/boards_responses.rb deleted file mode 100644 index eb7392648a1..00000000000 --- a/app/controllers/concerns/boards_responses.rb +++ /dev/null @@ -1,94 +0,0 @@ -# frozen_string_literal: true - -module BoardsResponses - include Gitlab::Utils::StrongMemoize - - # Overridden on EE module - def board_params - params.require(:board).permit(:name) - end - - def parent - strong_memoize(:parent) do - group? ? group : project - end - end - - def boards_path - if group? - group_boards_path(parent) - else - project_boards_path(parent) - end - end - - def board_path(board) - if group? - group_board_path(parent, board) - else - project_board_path(parent, board) - end - end - - def group? - instance_variable_defined?(:@group) - end - - def authorize_read_list - authorize_action_for!(board, :read_issue_board_list) - end - - def authorize_read_issue - authorize_action_for!(board, :read_issue) - end - - def authorize_update_issue - authorize_action_for!(issue, :admin_issue) - end - - def authorize_create_issue - list = List.find(issue_params[:list_id]) - action = list.backlog? ? :create_issue : :admin_issue - - authorize_action_for!(project, action) - end - - def authorize_admin_list - authorize_action_for!(board, :admin_issue_board_list) - end - - def authorize_action_for!(resource, ability) - return render_403 unless can?(current_user, ability, resource) - end - - def respond_with_boards - respond_with(@boards) # rubocop:disable Gitlab/ModuleWithInstanceVariables - end - - def respond_with_board - # rubocop:disable Gitlab/ModuleWithInstanceVariables - return render_404 unless @board - - respond_with(@board) - # rubocop:enable Gitlab/ModuleWithInstanceVariables - end - - def serialize_as_json(resource) - serializer.represent(resource).as_json - end - - def respond_with(resource) - respond_to do |format| - format.html - format.json do - render json: serialize_as_json(resource) - end - end - end - - def serializer - BoardSerializer.new - end -end - -BoardsResponses.prepend_mod_with('BoardsResponses') diff --git a/app/controllers/concerns/import/github_oauth.rb b/app/controllers/concerns/import/github_oauth.rb new file mode 100644 index 00000000000..d53022aabf2 --- /dev/null +++ b/app/controllers/concerns/import/github_oauth.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +module Import + module GithubOauth + extend ActiveSupport::Concern + + OAuthConfigMissingError = Class.new(StandardError) + + included do + rescue_from OAuthConfigMissingError, with: :missing_oauth_config + end + + private + + def provider_auth + return if session[access_token_key].present? + + go_to_provider_for_permissions unless ci_cd_only? + end + + def ci_cd_only? + %w[1 true].include?(params[:ci_cd_only]) + end + + def go_to_provider_for_permissions + redirect_to authorize_url + end + + def oauth_client + raise OAuthConfigMissingError unless oauth_config + + oauth_client_from_config + end + + def oauth_client_from_config + @oauth_client_from_config ||= ::OAuth2::Client.new( + oauth_config.app_id, + oauth_config.app_secret, + oauth_options.merge(ssl: { verify: oauth_config['verify_ssl'] }) + ) + end + + def oauth_config + @oauth_config ||= Gitlab::Auth::OAuth::Provider.config_for('github') + end + + def oauth_options + return unless oauth_config + + oauth_config.dig('args', 'client_options').deep_symbolize_keys + end + + def authorize_url + state = SecureRandom.base64(64) + session[auth_state_key] = state + if Feature.enabled?(:remove_legacy_github_client) + oauth_client.auth_code.authorize_url( + redirect_uri: callback_import_url, + scope: 'repo, user, user:email', + state: state + ) + else + client.authorize_url(callback_import_url, state) + end + end + + def get_token(code) + if Feature.enabled?(:remove_legacy_github_client) + oauth_client.auth_code.get_token(code).token + else + client.get_token(code) + end + end + + def missing_oauth_config + session[access_token_key] = nil + + message = _('Missing OAuth configuration for GitHub.') + + respond_to do |format| + format.json do + render json: { errors: message }, status: :unauthorized + end + + format.any do + redirect_to new_import_url, + alert: message + end + end + end + + def callback_import_url + public_send("users_import_#{provider_name}_callback_url", extra_import_params.merge({ namespace_id: params[:namespace_id] })) # rubocop:disable GitlabSecurity/PublicSend + end + + def extra_import_params + {} + end + end +end diff --git a/app/controllers/concerns/issuable_collections_action.rb b/app/controllers/concerns/issuable_collections_action.rb index 96cf6021ea9..e03d1de7bf9 100644 --- a/app/controllers/concerns/issuable_collections_action.rb +++ b/app/controllers/concerns/issuable_collections_action.rb @@ -59,9 +59,12 @@ module IssuableCollectionsAction end def finder_options + issue_types = Issue::TYPES_FOR_LIST + issue_types = issue_types.excluding('task') unless Feature.enabled?(:work_items) + super.merge( non_archived: true, - issue_types: Issue::TYPES_FOR_LIST + issue_types: issue_types ) end end diff --git a/app/controllers/concerns/multiple_boards_actions.rb b/app/controllers/concerns/multiple_boards_actions.rb deleted file mode 100644 index 685c93fc2a2..00000000000 --- a/app/controllers/concerns/multiple_boards_actions.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true - -module MultipleBoardsActions - include Gitlab::Utils::StrongMemoize - extend ActiveSupport::Concern - - included do - include BoardsActions - - before_action :redirect_to_recent_board, only: [:index] - before_action :authenticate_user!, only: [:recent] - before_action :authorize_create_board!, only: [:create] - before_action :authorize_admin_board!, only: [:create, :update, :destroy] - end - - def recent - recent_visits = ::Boards::VisitsFinder.new(parent, current_user).latest(Board::RECENT_BOARDS_SIZE) - recent_boards = recent_visits.map(&:board) - - render json: serialize_as_json(recent_boards) - end - - def create - response = Boards::CreateService.new(parent, current_user, board_params).execute - - respond_to do |format| - format.json do - board = response.payload - - if response.success? - extra_json = { board_path: board_path(board) } - render json: serialize_as_json(board).merge(extra_json) - else - render json: board.errors, status: :unprocessable_entity - end - end - end - end - - def update - service = Boards::UpdateService.new(parent, current_user, board_params) - - respond_to do |format| - format.json do - if service.execute(board) - extra_json = { board_path: board_path(board) } - render json: serialize_as_json(board).merge(extra_json) - else - render json: board.errors, status: :unprocessable_entity - end - end - end - end - - def destroy - service = Boards::DestroyService.new(parent, current_user) - service.execute(board) - - respond_to do |format| - format.json { head :ok } - format.html { redirect_to boards_path, status: :found } - end - end - - private - - def redirect_to_recent_board - return unless board_type == Board.to_type - return if request.format.json? || !parent.multiple_issue_boards_available? || !latest_visited_board - - redirect_to board_path(latest_visited_board.board) - end - - def latest_visited_board - @latest_visited_board ||= Boards::VisitsFinder.new(parent, current_user).latest - end - - def authorize_create_board! - check_multiple_group_issue_boards_available! if group? - end - - def authorize_admin_board! - return render_404 unless can?(current_user, :admin_issue_board, parent) - end - - def serializer - BoardSerializer.new(current_user: current_user) - end - - def serialize_as_json(resource) - serializer.represent(resource, serializer: 'board', include_full_project_path: board.group_board?) - end -end diff --git a/app/controllers/concerns/preview_markdown.rb b/app/controllers/concerns/preview_markdown.rb index 1d2f9e31c46..79b3fa28660 100644 --- a/app/controllers/concerns/preview_markdown.rb +++ b/app/controllers/concerns/preview_markdown.rb @@ -26,16 +26,24 @@ module PreviewMarkdown } end + def timeline_events_filter_params + { + issuable_reference_expansion_enabled: true, + pipeline: :'incident_management/timeline_event' + } + end + def markdown_service_params params end def markdown_context_params case controller_name - when 'wikis' then { pipeline: :wiki, wiki: wiki, page_slug: params[:id] } - when 'snippets' then { skip_project_check: true } - when 'groups' then { group: group } - when 'projects' then projects_filter_params + when 'wikis' then { pipeline: :wiki, wiki: wiki, page_slug: params[:id] } + when 'snippets' then { skip_project_check: true } + when 'groups' then { group: group } + when 'projects' then projects_filter_params + when 'timeline_events' then timeline_events_filter_params else {} end.merge(requested_path: params[:path], ref: params[:ref]) end diff --git a/app/controllers/concerns/product_analytics_tracking.rb b/app/controllers/concerns/product_analytics_tracking.rb index 8e936782e5a..4f96cc5c895 100644 --- a/app/controllers/concerns/product_analytics_tracking.rb +++ b/app/controllers/concerns/product_analytics_tracking.rb @@ -29,7 +29,13 @@ module ProductAnalyticsTracking track_unique_redis_hll_event(name, &block) if destinations.include?(:redis_hll) if destinations.include?(:snowplow) && event_enabled?(name) - Gitlab::Tracking.event(self.class.to_s, name, namespace: tracking_namespace_source, user: current_user) + Gitlab::Tracking.event( + self.class.to_s, + name, + namespace: tracking_namespace_source, + user: current_user, + context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: name).to_context] + ) end end @@ -49,6 +55,7 @@ module ProductAnalyticsTracking user: current_user, property: name, label: label, + context: [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll, event: name).to_context], **optional_arguments ) end diff --git a/app/controllers/concerns/registrations_tracking.rb b/app/controllers/concerns/registrations_tracking.rb new file mode 100644 index 00000000000..14743349c1a --- /dev/null +++ b/app/controllers/concerns/registrations_tracking.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module RegistrationsTracking + extend ActiveSupport::Concern + + included do + helper_method :glm_tracking_params + end + + private + + def glm_tracking_params + params.permit(:glm_source, :glm_content) + end +end diff --git a/app/controllers/concerns/sends_blob.rb b/app/controllers/concerns/sends_blob.rb index 381f2eba352..3cf260c9f1b 100644 --- a/app/controllers/concerns/sends_blob.rb +++ b/app/controllers/concerns/sends_blob.rb @@ -27,12 +27,14 @@ module SendsBlob private def cached_blob?(blob, allow_caching: false) - stale = stale?(etag: blob.id) # The #stale? method sets cache headers. - - # Because we are opinionated we set the cache headers ourselves. - response.cache_control[:public] = allow_caching + stale = + if Feature.enabled?(:improve_blobs_cache_headers) + stale?(strong_etag: blob.id) + else + stale?(etag: blob.id) + end - response.cache_control[:max_age] = + max_age = if @ref && @commit && @ref == @commit.id # rubocop:disable Gitlab/ModuleWithInstanceVariables # This is a link to a commit by its commit SHA. That means that the blob # is immutable. The only reason to invalidate the cache is if the commit @@ -44,6 +46,16 @@ module SendsBlob Blob::CACHE_TIME end + # Because we are opinionated we set the cache headers ourselves. + if Feature.enabled?(:improve_blobs_cache_headers) + expires_in(max_age, + public: allow_caching, must_revalidate: true, stale_if_error: 5.minutes, + stale_while_revalidate: 1.minute, 's-maxage': 1.minute) + else + response.cache_control[:public] = allow_caching + response.cache_control[:max_age] = max_age + end + !stale end diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb index 83447744013..2b781c528ad 100644 --- a/app/controllers/concerns/wiki_actions.rb +++ b/app/controllers/concerns/wiki_actions.rb @@ -209,9 +209,7 @@ module WikiActions def wiki strong_memoize(:wiki) do wiki = Wiki.for_container(container, current_user) - - # Call #wiki to make sure the Wiki Repo is initialized - wiki.wiki + wiki.create_wiki_repository wiki end @@ -242,7 +240,7 @@ module WikiActions def wiki_pages strong_memoize(:wiki_pages) do Kaminari.paginate_array( - wiki.list_pages(sort: params[:sort], direction: params[:direction]) + wiki.list_pages(direction: params[:direction]) ).page(params[:page]) end end diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb index aec3247f4b2..f8cfa996447 100644 --- a/app/controllers/groups/application_controller.rb +++ b/app/controllers/groups/application_controller.rb @@ -67,6 +67,10 @@ class Groups::ApplicationController < ApplicationController end end + def authorize_billings_page! + render_404 unless can?(current_user, :read_billing, group) + end + def authorize_read_group_member! unless can?(current_user, :read_group_member, group) render_403 diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb index e64d838b7d1..14b70df0feb 100644 --- a/app/controllers/groups/boards_controller.rb +++ b/app/controllers/groups/boards_controller.rb @@ -5,7 +5,6 @@ class Groups::BoardsController < Groups::ApplicationController include RecordUserLastActivity include Gitlab::Utils::StrongMemoize - before_action :assign_endpoint_vars before_action do push_frontend_feature_flag(:board_multi_select, group) push_frontend_feature_flag(:realtime_labels, group) @@ -20,16 +19,6 @@ class Groups::BoardsController < Groups::ApplicationController private - def board_klass - Board - end - - def boards_finder - strong_memoize :boards_finder do - Boards::BoardsFinder.new(parent, current_user) - end - end - def board_finder strong_memoize :board_finder do Boards::BoardsFinder.new(parent, current_user, board_id: params[:id]) @@ -42,10 +31,6 @@ class Groups::BoardsController < Groups::ApplicationController end end - def assign_endpoint_vars - @boards_endpoint = group_boards_path(group) - end - def authorize_read_board! access_denied! unless can?(current_user, :read_issue_board, group) end diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb index 652f12e34ba..18b055b3f05 100644 --- a/app/controllers/groups/runners_controller.rb +++ b/app/controllers/groups/runners_controller.rb @@ -2,13 +2,9 @@ class Groups::RunnersController < Groups::ApplicationController before_action :authorize_read_group_runners!, only: [:index, :show] - before_action :authorize_admin_group_runners!, only: [:edit, :update, :destroy, :pause, :resume] + before_action :authorize_update_runner!, 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 @@ -37,7 +33,9 @@ class Groups::RunnersController < Groups::ApplicationController private def runner - @runner ||= Ci::RunnersFinder.new(current_user: current_user, params: { group: @group }).execute + group_params = { group: @group, membership: :all_available } + + @runner ||= Ci::RunnersFinder.new(current_user: current_user, params: group_params).execute .except(:limit, :offset) .find(params[:id]) end @@ -45,6 +43,12 @@ class Groups::RunnersController < Groups::ApplicationController def runner_params params.require(:runner).permit(Ci::Runner::FORM_EDITABLE) end + + def authorize_update_runner! + return if can?(current_user, :admin_group_runners, group) && can?(current_user, :update_runner, runner) + + render_404 + end end Groups::RunnersController.prepend_mod diff --git a/app/controllers/groups/settings/access_tokens_controller.rb b/app/controllers/groups/settings/access_tokens_controller.rb index b9ab2e008cc..f01b2b779e3 100644 --- a/app/controllers/groups/settings/access_tokens_controller.rb +++ b/app/controllers/groups/settings/access_tokens_controller.rb @@ -13,6 +13,12 @@ module Groups def resource_access_tokens_path group_settings_access_tokens_path end + + private + + def represent(tokens) + ::GroupAccessTokenSerializer.new.represent(tokens, group: resource) + end end end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 9316204d89c..269342a6c22 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -81,9 +81,9 @@ class GroupsController < Groups::ApplicationController successful_creation_hooks notice = if @group.chat_team.present? - "Group '#{@group.name}' and its Mattermost team were successfully created." + format(_("Group %{group_name} and its Mattermost team were successfully created."), group_name: @group.name) else - "Group '#{@group.name}' was successfully created." + format(_("Group %{group_name} was successfully created."), group_name: @group.name) end redirect_to @group, notice: notice @@ -393,7 +393,7 @@ class GroupsController < Groups::ApplicationController end def captcha_enabled? - Gitlab::Recaptcha.enabled? && Feature.enabled?(:recaptcha_on_top_level_group_creation, type: :ops) + helpers.recaptcha_enabled? && Feature.enabled?(:recaptcha_on_top_level_group_creation, type: :ops) end def captcha_required? diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb index 58a985cbc46..fcf6871d137 100644 --- a/app/controllers/ide_controller.rb +++ b/app/controllers/ide_controller.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true class IdeController < ApplicationController - layout 'fullscreen' - include ClientsidePreviewCSP include StaticObjectExternalStorageCSP include Gitlab::Utils::StrongMemoize @@ -13,7 +11,6 @@ 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 @@ -28,6 +25,8 @@ class IdeController < ApplicationController Gitlab::Tracking.event(self.class.to_s, 'web_ide_views', namespace: project&.namespace, user: current_user) end + + render layout: 'fullscreen', locals: { minimal: helpers.use_new_web_ide? } end private diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb index 893c0b6ac54..655fc7854fe 100644 --- a/app/controllers/import/bulk_imports_controller.rb +++ b/app/controllers/import/bulk_imports_controller.rb @@ -47,6 +47,8 @@ class Import::BulkImportsController < ApplicationController end def create + return render json: { success: false }, status: :unprocessable_entity unless valid_create_params? + responses = create_params.map do |entry| if entry[:destination_name] entry[:destination_slug] ||= entry[:destination_name] @@ -102,6 +104,10 @@ class Import::BulkImportsController < ApplicationController params.permit(bulk_import: bulk_import_params)[:bulk_import] end + def valid_create_params? + create_params.all? { _1[:source_type] == 'group_entity' } + end + def bulk_import_params %i[ source_type @@ -113,7 +119,7 @@ class Import::BulkImportsController < ApplicationController end def ensure_group_import_enabled - render_404 unless Feature.enabled?(:bulk_import) + render_404 unless ::BulkImports::Features.enabled? end def access_token_key diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index 8a3e6809736..92763e09ba3 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -5,14 +5,12 @@ class Import::GithubController < Import::BaseController include ImportHelper include ActionView::Helpers::SanitizeHelper + include Import::GithubOauth before_action :verify_import_enabled before_action :provider_auth, only: [:status, :realtime_changes, :create] before_action :expire_etag_cache, only: [:status, :create] - OAuthConfigMissingError = Class.new(StandardError) - - rescue_from OAuthConfigMissingError, with: :missing_oauth_config rescue_from Octokit::Unauthorized, with: :provider_unauthorized rescue_from Octokit::TooManyRequests, with: :provider_rate_limit rescue_from Gitlab::GithubImport::RateLimitError, with: :rate_limit_threshold_exceeded @@ -73,6 +71,17 @@ class Import::GithubController < Import::BaseController } end + def cancel + project = Project.imported_from(provider_name).find(params[:project_id]) + result = Import::Github::CancelProjectImportService.new(project, current_user).execute + + if result[:status] == :success + render json: serialized_imported_projects(result[:project]) + else + render json: { errors: result[:message] }, status: result[:http_status] + end + end + protected override :importable_repos @@ -104,7 +113,7 @@ class Import::GithubController < Import::BaseController end def permitted_import_params - [:repo_id, :new_name, :target_namespace] + [:repo_id, :new_name, :target_namespace, { optional_stages: {} }] end def serialized_imported_projects(projects = already_added_projects) @@ -143,58 +152,10 @@ class Import::GithubController < Import::BaseController @filter = @filter&.tr(' ', '')&.tr(':', '') end - def oauth_client - raise OAuthConfigMissingError unless oauth_config - - @oauth_client ||= ::OAuth2::Client.new( - oauth_config.app_id, - oauth_config.app_secret, - oauth_options.merge(ssl: { verify: oauth_config['verify_ssl'] }) - ) - end - - def oauth_config - @oauth_config ||= Gitlab::Auth::OAuth::Provider.config_for('github') - end - - def oauth_options - if oauth_config - oauth_config.dig('args', 'client_options').deep_symbolize_keys - else - OmniAuth::Strategies::GitHub.default_options[:client_options].symbolize_keys - end - end - - def authorize_url - state = SecureRandom.base64(64) - session[auth_state_key] = state - if Feature.enabled?(:remove_legacy_github_client) - oauth_client.auth_code.authorize_url( - redirect_uri: callback_import_url, - scope: 'repo, user, user:email', - state: state - ) - else - client.authorize_url(callback_import_url, state) - end - end - - def get_token(code) - if Feature.enabled?(:remove_legacy_github_client) - oauth_client.auth_code.get_token(code).token - else - client.get_token(code) - end - end - def verify_import_enabled render_404 unless import_enabled? end - def go_to_provider_for_permissions - redirect_to authorize_url - end - def import_enabled? __send__("#{provider_name}_import_enabled?") # rubocop:disable GitlabSecurity/PublicSend end @@ -211,10 +172,6 @@ class Import::GithubController < Import::BaseController public_send("status_import_#{provider_name}_url", extra_import_params.merge({ namespace_id: params[:namespace_id].presence })) # rubocop:disable GitlabSecurity/PublicSend end - def callback_import_url - public_send("users_import_#{provider_name}_callback_url", extra_import_params.merge({ namespace_id: params[:namespace_id] })) # rubocop:disable GitlabSecurity/PublicSend - end - def provider_unauthorized session[access_token_key] = nil redirect_to new_import_url, @@ -228,12 +185,6 @@ class Import::GithubController < Import::BaseController alert: _("GitHub API rate limit exceeded. Try again after %{reset_time}") % { reset_time: reset_time } end - def missing_oauth_config - session[access_token_key] = nil - redirect_to new_import_url, - alert: _('Missing OAuth configuration for GitHub.') - end - def auth_state_key :"#{provider_name}_auth_state_key" end @@ -252,24 +203,10 @@ class Import::GithubController < Import::BaseController end # rubocop: enable CodeReuse/ActiveRecord - def provider_auth - if !ci_cd_only? && session[access_token_key].blank? - go_to_provider_for_permissions - end - end - - def ci_cd_only? - %w[1 true].include?(params[:ci_cd_only]) - end - def client_options { wait_for_rate_limit_reset: false } end - def extra_import_params - {} - end - def rate_limit_threshold_exceeded head :too_many_requests end diff --git a/app/controllers/import/github_groups_controller.rb b/app/controllers/import/github_groups_controller.rb new file mode 100644 index 00000000000..6c0773bcfb3 --- /dev/null +++ b/app/controllers/import/github_groups_controller.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module Import + class GithubGroupsController < ApplicationController + include Import::GithubOauth + + before_action :provider_auth, only: [:status] + feature_category :importers + + PAGE_LENGTH = 25 + + def status + respond_to do |format| + format.json do + render json: { provider_groups: serialized_provider_groups } + end + end + end + + private + + def serialized_provider_groups + Import::GithubOrgSerializer.new.represent(importable_orgs) + end + + def importable_orgs + client_orgs.to_a + end + + def client_orgs + @client_orgs ||= client.octokit.organizations(nil, pagination_options) + end + + def client + @client ||= Gitlab::GithubImport::Client.new(session[access_token_key]) + end + + def pagination_options + { + page: [1, params[:page].to_i].max, + per_page: PAGE_LENGTH + } + end + + def auth_state_key + :"#{provider_name}_auth_state_key" + end + + def access_token_key + :"#{provider_name}_access_token" + end + + def provider_name + :github + end + end +end diff --git a/app/controllers/jira_connect/public_keys_controller.rb b/app/controllers/jira_connect/public_keys_controller.rb new file mode 100644 index 00000000000..b3144993edb --- /dev/null +++ b/app/controllers/jira_connect/public_keys_controller.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module JiraConnect + class PublicKeysController < ::ApplicationController + # This is not inheriting from JiraConnect::Application controller because + # it doesn't need to handle JWT authentication. + + feature_category :integrations + + skip_before_action :authenticate_user! + + def show + return render_404 if Feature.disabled?(:jira_connect_oauth_self_managed) || !Gitlab.com? + + render plain: public_key.key + end + + private + + def public_key + JiraConnect::PublicKey.find(params[:id]) + end + end +end diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb index ff466fd5fbb..3b78b997da1 100644 --- a/app/controllers/oauth/applications_controller.rb +++ b/app/controllers/oauth/applications_controller.rb @@ -4,7 +4,6 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController include Gitlab::GonHelper include PageLayoutHelper include OauthApplications - include Gitlab::Experimentation::ControllerConcern include InitializesCurrentUserMode # Defined by the `Doorkeeper::ApplicationsController` and is redundant as we call `authenticate_user!` below. Not diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb index 2e9fbb1d0d9..bf8b61db2e5 100644 --- a/app/controllers/oauth/authorizations_controller.rb +++ b/app/controllers/oauth/authorizations_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController - include Gitlab::Experimentation::ControllerConcern include InitializesCurrentUserMode include Gitlab::Utils::StrongMemoize diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 817f272d458..f3f0ddd968a 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -181,6 +181,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController end accept_pending_invitations(user: user) if new_user + persist_accepted_terms_if_required(user) if new_user + store_after_sign_up_path_for_user if intent_to_register? sign_in_and_redirect(user, event: :authentication) end @@ -301,6 +303,15 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController redirect_to new_admin_session_path, alert: _('Invalid login or password') end + def persist_accepted_terms_if_required(user) + return unless Feature.enabled?(:update_oauth_registration_flow) + return unless user.persisted? + return unless Gitlab::CurrentSettings.current_application_settings.enforce_terms? + + terms = ApplicationSetting::Term.latest + Users::RespondToTermsService.new(user, terms).execute(accepted: true) + end + def store_after_sign_up_path_for_user store_location_for(:user, users_sign_up_welcome_path) end diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb index 8ed67c26f19..4cf26d3e1e2 100644 --- a/app/controllers/profiles/personal_access_tokens_controller.rb +++ b/app/controllers/profiles/personal_access_tokens_controller.rb @@ -3,6 +3,8 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController feature_category :authentication_and_authorization + before_action :check_personal_access_tokens_enabled + def index set_index_vars scopes = params[:scopes].split(',').map(&:squish).select(&:present?).map(&:to_sym) unless params[:scopes].nil? @@ -83,4 +85,8 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController def page (params[:page] || 1).to_i end + + def check_personal_access_tokens_enabled + render_404 if Gitlab::CurrentSettings.personal_access_tokens_disabled? + end end diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb index 7aca76c2fb1..a57c87bf691 100644 --- a/app/controllers/profiles/preferences_controller.rb +++ b/app/controllers/profiles/preferences_controller.rb @@ -55,7 +55,9 @@ class Profiles::PreferencesController < Profiles::ApplicationController :sourcegraph_enabled, :gitpod_enabled, :render_whitespace_in_code, - :markdown_surround_selection + :markdown_surround_selection, + :markdown_automatic_lists, + :use_legacy_web_ide ] end end diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb index 0b7d4626c6d..0933f2bb7ea 100644 --- a/app/controllers/profiles/two_factor_auths_controller.rb +++ b/app/controllers/profiles/two_factor_auths_controller.rb @@ -15,31 +15,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController feature_category :authentication_and_authorization def show - if two_factor_authentication_required? && !current_user.two_factor_enabled? - two_factor_authentication_reason( - global: lambda do - flash.now[:alert] = - _('The global settings require you to enable Two-Factor Authentication for your account.') - end, - group: lambda do |groups| - flash.now[:alert] = groups_notification(groups) - end - ) - - unless two_factor_grace_period_expired? - grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours - flash.now[:alert] = flash.now[:alert] + _(" You need to do this before %{grace_period_deadline}.") % { grace_period_deadline: l(grace_period_deadline) } - end - end - - @qr_code = build_qr_code - @account_string = account_string - - if Feature.enabled?(:webauthn) - setup_webauthn_registration - else - setup_u2f_registration - end + setup_show_page end def create @@ -147,7 +123,11 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController current_user.increment_failed_attempts! - redirect_to profile_two_factor_auth_path, alert: _('You must provide a valid current password') + @error = { message: _('You must provide a valid current password') } + + setup_show_page + + render 'show' end def current_password_required? @@ -245,4 +225,32 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController redirect_to profile_emails_path, notice: s_('You need to verify your primary email first before enabling Two-Factor Authentication.') end end + + def setup_show_page + if two_factor_authentication_required? && !current_user.two_factor_enabled? + two_factor_authentication_reason( + global: lambda do + flash.now[:alert] = + _('The global settings require you to enable Two-Factor Authentication for your account.') + end, + group: lambda do |groups| + flash.now[:alert] = groups_notification(groups) + end + ) + + unless two_factor_grace_period_expired? + grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours + flash.now[:alert] = flash.now[:alert] + _(" You need to do this before %{grace_period_deadline}.") % { grace_period_deadline: l(grace_period_deadline) } + end + end + + @qr_code = build_qr_code + @account_string = account_string + + if Feature.enabled?(:webauthn) + setup_webauthn_registration + else + setup_u2f_registration + end + end end diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 028b7af02c9..2256471047d 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -39,8 +39,8 @@ class Projects::ApplicationController < ApplicationController access_denied!( _('You must have developer or higher permissions in the associated project to view job logs when debug trace ' \ "is enabled. To disable debug trace, set the 'CI_DEBUG_TRACE' variable to 'false' in your pipeline " \ - 'configuration or CI/CD settings. If you need to view this job log, a project maintainer must add you to ' \ - 'the project with developer permissions or higher.') + 'configuration or CI/CD settings. If you need to view this job log, a project maintainer or owner must add ' \ + 'you to the project with developer permissions or higher.') ) else access_denied!(_('The current user is not authorized to access the job log.')) diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb index 9dbf989ca3f..7755effe1da 100644 --- a/app/controllers/projects/autocomplete_sources_controller.rb +++ b/app/controllers/projects/autocomplete_sources_controller.rb @@ -41,7 +41,7 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController end def contacts - render json: autocomplete_service.contacts + render json: autocomplete_service.contacts(target) end private @@ -51,9 +51,12 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController end def target + # type_id is not required in general + target_type = params.require(:type) + QuickActions::TargetService .new(project, current_user) - .execute(params[:type], params[:type_id]) + .execute(target_type, params[:type_id]) end def authorize_read_crm_contact! diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index 2a20c67a23d..01ed5473b41 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -26,7 +26,10 @@ class Projects::BlameController < Projects::ApplicationController 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! + @blame_pagination = blame_service.pagination + + @blame_per_page = blame_service.per_page end end diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb index 82b35a22669..6a6701ead15 100644 --- a/app/controllers/projects/boards_controller.rb +++ b/app/controllers/projects/boards_controller.rb @@ -1,11 +1,10 @@ # frozen_string_literal: true class Projects::BoardsController < Projects::ApplicationController - include MultipleBoardsActions + include BoardsActions include IssuableCollections before_action :check_issues_available! - before_action :assign_endpoint_vars before_action do push_frontend_feature_flag(:board_multi_select, project) push_frontend_feature_flag(:realtime_labels, project&.group) @@ -20,16 +19,6 @@ class Projects::BoardsController < Projects::ApplicationController private - def board_klass - Board - end - - def boards_finder - strong_memoize :boards_finder do - Boards::BoardsFinder.new(parent, current_user) - end - end - def board_finder strong_memoize :board_finder do Boards::BoardsFinder.new(parent, current_user, board_id: params[:id]) @@ -42,11 +31,6 @@ class Projects::BoardsController < Projects::ApplicationController end end - def assign_endpoint_vars - @boards_endpoint = project_boards_path(project) - @bulk_issues_path = bulk_update_project_issues_path(project) - end - def authorize_read_board! access_denied! unless can?(current_user, :read_issue_board, project) end diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index d7fd65f02a8..61308f24412 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -47,7 +47,8 @@ class Projects::CompareController < Projects::ApplicationController from_to_vars = { from: compare_params[:from].presence, to: compare_params[:to].presence, - from_project_id: compare_params[:from_project_id].presence + from_project_id: compare_params[:from_project_id].presence, + straight: compare_params[:straight].presence } if from_to_vars[:from].blank? || from_to_vars[:to].blank? @@ -112,7 +113,11 @@ class Projects::CompareController < Projects::ApplicationController def compare return @compare if defined?(@compare) - @compare = CompareService.new(source_project, head_ref).execute(target_project, start_ref) + @compare = CompareService.new(source_project, head_ref).execute(target_project, start_ref, straight: straight) + end + + def straight + compare_params[:straight] == "true" end def start_ref @@ -160,6 +165,6 @@ class Projects::CompareController < Projects::ApplicationController # rubocop: enable CodeReuse/ActiveRecord def compare_params - @compare_params ||= params.permit(:from, :to, :from_project_id) + @compare_params ||= params.permit(:from, :to, :from_project_id, :straight) end end diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index 96afe9dbb9f..22a42d22914 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -27,11 +27,9 @@ class Projects::DeployKeysController < Projects::ApplicationController end def create - @key = DeployKeys::CreateService.new(current_user, create_params).execute(project: @project) + @key = DeployKeys::CreateService.new(current_user, create_params).execute(project: @project).present - unless @key.valid? - flash[:alert] = @key.errors.full_messages.join(', ').html_safe - end + flash[:alert] = @key.humanized_error_message unless @key.valid? redirect_to_repository end diff --git a/app/controllers/projects/google_cloud/databases_controller.rb b/app/controllers/projects/google_cloud/databases_controller.rb index 8f7554f248b..77ee830fd24 100644 --- a/app/controllers/projects/google_cloud/databases_controller.rb +++ b/app/controllers/projects/google_cloud/databases_controller.rb @@ -50,16 +50,15 @@ module Projects 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)) + .new(project, current_user, create_service_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) + track_event(:create_cloudsql_instance, permitted_params_create.to_s) flash[:notice] = success_message end end @@ -69,17 +68,25 @@ module Projects private + def permitted_params_create + params.permit(:gcp_project, :ref, :database_version, :tier) + end + def enable_service_params - { google_oauth2_token: token_in_session } + { + google_oauth2_token: token_in_session, + gcp_project_id: permitted_params_create[:gcp_project], + environment_name: permitted_params_create[:ref] + } end - def create_service_params(permitted_params) + def create_service_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] + gcp_project_id: permitted_params_create[:gcp_project], + environment_name: permitted_params_create[:ref], + database_version: permitted_params_create[:database_version], + tier: permitted_params_create[:tier] } end diff --git a/app/controllers/projects/incident_management/timeline_events_controller.rb b/app/controllers/projects/incident_management/timeline_events_controller.rb new file mode 100644 index 00000000000..7e7a4758e48 --- /dev/null +++ b/app/controllers/projects/incident_management/timeline_events_controller.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Projects + module IncidentManagement + class TimelineEventsController < Projects::ApplicationController + include PreviewMarkdown + + before_action :authenticate_user! + + respond_to :json + + feature_category :incident_management + urgency :low + end + end +end diff --git a/app/controllers/projects/incidents_controller.rb b/app/controllers/projects/incidents_controller.rb index cbf0c756e1e..089ee860ea6 100644 --- a/app/controllers/projects/incidents_controller.rb +++ b/app/controllers/projects/incidents_controller.rb @@ -7,11 +7,9 @@ class Projects::IncidentsController < Projects::ApplicationController before_action :authorize_read_issue! before_action :load_incident, only: [:show] before_action do - push_frontend_feature_flag(:incident_timeline, @project) 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 diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 800a7df2566..5b1117c0224 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -41,8 +41,8 @@ class Projects::IssuesController < Projects::ApplicationController before_action :authorize_download_code!, only: [:related_branches] before_action do - push_frontend_feature_flag(:incident_timeline, project) - push_frontend_feature_flag(:remove_user_attributes_projects, project) + push_frontend_feature_flag(:preserve_unchanged_markdown, project) + push_frontend_feature_flag(:content_editor_on_issues, project) end before_action only: [:index, :show] do @@ -147,19 +147,26 @@ class Projects::IssuesController < Projects::ApplicationController spam_params = ::Spam::SpamParams.new_from_request(request: request) service = ::Issues::CreateService.new(project: project, current_user: current_user, params: create_params, spam_params: spam_params) - @issue = service.execute + result = service.execute - create_vulnerability_issue_feedback(issue) - - if service.discussions_to_resolve.count(&:resolved?) > 0 - flash[:notice] = if service.discussion_to_resolve_id - _("Resolved 1 discussion.") - else - _("Resolved all discussions.") - end + # Only irrecoverable errors such as unauthorized user won't contain an issue in the response + if result.error? && result[:issue].blank? + render_by_create_result_error(result) && return end - if @issue.valid? + @issue = result[:issue] + + if result.success? + create_vulnerability_issue_feedback(@issue) + + if service.discussions_to_resolve.count(&:resolved?) > 0 + flash[:notice] = if service.discussion_to_resolve_id + _("Resolved 1 discussion.") + else + _("Resolved all discussions.") + end + end + redirect_to project_issue_path(@project, @issue) else # NOTE: this CAPTCHA support method is indirectly included via IssuableActions @@ -372,6 +379,21 @@ class Projects::IssuesController < Projects::ApplicationController private + def render_by_create_result_error(result) + Gitlab::AppLogger.warn( + message: 'Cannot create issue', + errors: result.errors, + http_status: result.http_status + ) + error_method_name = "render_#{result.http_status}".to_sym + + if respond_to?(error_method_name, true) + send(error_method_name) # rubocop:disable GitlabSecurity/PublicSend + else + render_404 + end + end + def clean_params(all_params) issue_type = all_params[:issue_type].to_s all_params.delete(:issue_type) unless WorkItems::Type.allowed_types_for_issues.include?(issue_type) @@ -383,6 +405,7 @@ class Projects::IssuesController < Projects::ApplicationController options = super options[:issue_types] = Issue::TYPES_FOR_LIST + options[:issue_types] = options[:issue_types].excluding('task') unless project.work_items_feature_flag_enabled? if service_desk? options.reject! { |key| key == 'author_username' || key == 'author_id' } diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index a68c2ffa06d..418e7233e21 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -4,6 +4,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic include DiffHelper include RendersNotes include Gitlab::Cache::Helpers + include Gitlab::Tracking::Helpers before_action :commit before_action :define_diff_vars diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 5a212e9a152..9c139733248 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -34,7 +34,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo before_action only: [:show] do push_frontend_feature_flag(:merge_request_widget_graphql, project) push_frontend_feature_flag(:core_security_mr_widget_counts, project) - push_frontend_feature_flag(:refactor_mr_widgets_extensions, project) push_frontend_feature_flag(:refactor_code_quality_extension, project) push_frontend_feature_flag(:refactor_mr_widget_test_summary, project) push_frontend_feature_flag(:issue_assignees_widget, @project) @@ -45,7 +44,6 @@ 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 @@ -451,15 +449,16 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo return :failed end + squashing = params.fetch(:squash, false) merge_service = ::MergeRequests::MergeService.new(project: @project, current_user: current_user, params: merge_params) - unless merge_service.hooks_validation_pass?(@merge_request) + unless merge_service.hooks_validation_pass?(@merge_request, validate_squash_message: squashing) return :hook_validation_error end return :sha_mismatch if params[:sha] != @merge_request.diff_head_sha - @merge_request.update(merge_error: nil, squash: params.fetch(:squash, false)) + @merge_request.update(merge_error: nil, squash: squashing) if auto_merge_requested? if merge_request.auto_merge_enabled? @@ -555,7 +554,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo end def endpoint_metadata_url(project, merge_request) - params = request.query_parameters.merge(view: 'inline', diff_head: true) + params = request.query_parameters.merge(view: 'inline', diff_head: true, w: current_user&.show_whitespace_in_diffs ? '0' : '1') diffs_metadata_project_json_merge_request_path(project, merge_request, 'json', params) end diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index cfb67b7b4ff..78108cf3478 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -4,8 +4,11 @@ class Projects::MilestonesController < Projects::ApplicationController include Gitlab::Utils::StrongMemoize include MilestoneActions + REDIRECT_TARGETS = [:new_release].freeze + before_action :check_issuables_available! before_action :milestone, only: [:edit, :update, :destroy, :show, :issues, :merge_requests, :participants, :labels, :promote] + before_action :redirect_path, only: [:new, :create] # Allow read any milestone before_action :authorize_read_milestone! @@ -59,7 +62,11 @@ class Projects::MilestonesController < Projects::ApplicationController @milestone = Milestones::CreateService.new(project, current_user, milestone_params).execute if @milestone.valid? - redirect_to project_milestone_path(@project, @milestone) + if @redirect_path == :new_release + redirect_to new_project_release_path(@project) + else + redirect_to project_milestone_path(@project, @milestone) + end else render "new" end @@ -113,6 +120,11 @@ class Projects::MilestonesController < Projects::ApplicationController protected + def redirect_path + path = params[:redirect_path]&.to_sym + @redirect_path = path if REDIRECT_TARGETS.include?(path) + end + def project_group strong_memoize(:project_group) do project.group diff --git a/app/controllers/projects/pages_domains_controller.rb b/app/controllers/projects/pages_domains_controller.rb index a6b22a28b17..43952a2efe4 100644 --- a/app/controllers/projects/pages_domains_controller.rb +++ b/app/controllers/projects/pages_domains_controller.rb @@ -41,9 +41,9 @@ class Projects::PagesDomainsController < Projects::ApplicationController end def create - @domain = @project.pages_domains.create(create_params) + @domain = PagesDomains::CreateService.new(@project, current_user, create_params).execute - if @domain.valid? + if @domain&.persisted? redirect_to project_pages_domain_path(@project, @domain) else render 'new' @@ -51,7 +51,9 @@ class Projects::PagesDomainsController < Projects::ApplicationController end def update - if @domain.update(update_params) + service = ::PagesDomains::UpdateService.new(@project, current_user, update_params) + + if service.execute(@domain) redirect_to project_pages_path(@project), status: :found, notice: 'Domain was updated' @@ -61,7 +63,9 @@ class Projects::PagesDomainsController < Projects::ApplicationController end def destroy - @domain.destroy + PagesDomains::DeleteService + .new(@project, current_user) + .execute(@domain) respond_to do |format| format.html do @@ -74,9 +78,10 @@ class Projects::PagesDomainsController < Projects::ApplicationController end def clean_certificate - unless @domain.update(user_provided_certificate: nil, user_provided_key: nil) - flash[:alert] = @domain.errors.full_messages.join(', ') - end + update_params = { user_provided_certificate: nil, user_provided_key: nil } + service = ::PagesDomains::UpdateService.new(@project, current_user, update_params) + + flash[:alert] = @domain.errors.full_messages.join(', ') unless service.execute(@domain) redirect_to project_pages_domain_path(@project, @domain) end diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb index a23d7fb3e6b..ca787785901 100644 --- a/app/controllers/projects/pipeline_schedules_controller.rb +++ b/app/controllers/projects/pipeline_schedules_controller.rb @@ -10,6 +10,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController before_action :authorize_update_pipeline_schedule!, only: [:edit, :update] before_action :authorize_take_ownership_pipeline_schedule!, only: [:take_ownership] before_action :authorize_admin_pipeline_schedule!, only: [:destroy] + before_action :push_schedule_feature_flag, only: [:index, :new, :edit] feature_category :continuous_integration urgency :low @@ -115,4 +116,8 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController def authorize_admin_pipeline_schedule! return access_denied! unless can?(current_user, :admin_pipeline_schedule, schedule) end + + def push_schedule_feature_flag + push_frontend_feature_flag(:pipeline_schedules_vue, @project) + end end diff --git a/app/controllers/projects/product_analytics_controller.rb b/app/controllers/projects/product_analytics_controller.rb index c89cd52530a..8085b0a6334 100644 --- a/app/controllers/projects/product_analytics_controller.rb +++ b/app/controllers/projects/product_analytics_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Projects::ProductAnalyticsController < Projects::ApplicationController - before_action :feature_enabled! + before_action :feature_enabled!, only: [:index, :setup, :test, :graphs] before_action :authorize_read_product_analytics! before_action :tracker_variables, only: [:setup, :test] @@ -57,3 +57,5 @@ class Projects::ProductAnalyticsController < Projects::ApplicationController render_404 unless Feature.enabled?(:product_analytics, @project) end end + +Projects::ProductAnalyticsController.prepend_mod_with('Projects::ProductAnalyticsController') diff --git a/app/controllers/projects/protected_refs_controller.rb b/app/controllers/projects/protected_refs_controller.rb index abbfe9ce22a..69a540158c6 100644 --- a/app/controllers/projects/protected_refs_controller.rb +++ b/app/controllers/projects/protected_refs_controller.rb @@ -4,7 +4,6 @@ class Projects::ProtectedRefsController < Projects::ApplicationController include RepositorySettingsRedirect # Authorize - before_action :require_non_empty_project before_action :authorize_admin_project! before_action :load_protected_ref, only: [:show, :update, :destroy] diff --git a/app/controllers/projects/settings/access_tokens_controller.rb b/app/controllers/projects/settings/access_tokens_controller.rb index 32916831ecd..bac35583a97 100644 --- a/app/controllers/projects/settings/access_tokens_controller.rb +++ b/app/controllers/projects/settings/access_tokens_controller.rb @@ -13,6 +13,12 @@ module Projects def resource_access_tokens_path namespace_project_settings_access_tokens_path end + + private + + def represent(tokens) + ::ProjectAccessTokenSerializer.new.represent(tokens, project: resource) + end end end end diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index c861b24d9ec..76e2da6eb57 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -14,7 +14,7 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController before_action :authorize_read_snippet!, except: [:new, :index] before_action :authorize_update_snippet!, only: :edit - urgency :low, [:index] + urgency :low, [:index, :show] def index @snippet_counts = ::Snippets::CountService diff --git a/app/controllers/projects/web_ide_terminals_controller.rb b/app/controllers/projects/web_ide_terminals_controller.rb index 350b091edfa..cfccc949244 100644 --- a/app/controllers/projects/web_ide_terminals_controller.rb +++ b/app/controllers/projects/web_ide_terminals_controller.rb @@ -10,6 +10,8 @@ class Projects::WebIdeTerminalsController < Projects::ApplicationController feature_category :web_ide + urgency :low, [:check_config] + def check_config return respond_422 unless branch_sha diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 5ceedbc1e01..b7b6e6534fb 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -311,8 +311,6 @@ 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') @@ -323,7 +321,7 @@ class ProjectsController < Projects::ApplicationController if find_branches branches = BranchesFinder.new(@repository, refs_params.merge(per_page: REFS_LIMIT)) - .execute(gitaly_pagination: use_gitaly_pagination) + .execute(gitaly_pagination: true) .take(REFS_LIMIT) .map(&:name) @@ -332,7 +330,7 @@ class ProjectsController < Projects::ApplicationController if find_tags && @repository.tag_count.nonzero? tags = TagsFinder.new(@repository, refs_params.merge(per_page: REFS_LIMIT)) - .execute(gitaly_pagination: use_gitaly_pagination) + .execute(gitaly_pagination: true) .take(REFS_LIMIT) .map(&:name) @@ -435,14 +433,14 @@ class ProjectsController < Projects::ApplicationController analytics_access_level security_and_compliance_access_level container_registry_access_level + releases_access_level ] + operations_feature_attributes end def operations_feature_attributes if Feature.enabled?(:split_operations_visibility_permissions, project) %i[ - environments_access_level feature_flags_access_level releases_access_level - monitor_access_level + environments_access_level feature_flags_access_level monitor_access_level ] else %i[operations_access_level] diff --git a/app/controllers/registrations/welcome_controller.rb b/app/controllers/registrations/welcome_controller.rb index 4e18e6a3b20..a49b82319da 100644 --- a/app/controllers/registrations/welcome_controller.rb +++ b/app/controllers/registrations/welcome_controller.rb @@ -4,6 +4,7 @@ module Registrations class WelcomeController < ApplicationController include OneTrustCSP include GoogleAnalyticsCSP + include RegistrationsTracking layout 'minimal' skip_before_action :authenticate_user!, :required_signup_info, :check_two_factor_requirement, only: [:show, :update] @@ -25,7 +26,7 @@ module Registrations members = current_user.members - if members.count == 1 && members.last.source.present? + if registering_from_invite?(members) redirect_to members_activity_path(members), notice: helpers.invite_accepted_notice(members.last) else redirect_to path_for_signed_in_user(current_user) @@ -37,6 +38,10 @@ module Registrations private + def registering_from_invite?(members) + members.count == 1 && members.last.source.present? + end + def require_current_user return redirect_to new_user_registration_path unless current_user end diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 0bd266bb490..31fe30f3f06 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -8,6 +8,7 @@ class RegistrationsController < Devise::RegistrationsController include OneTrustCSP include BizibleCSP include GoogleAnalyticsCSP + include RegistrationsTracking layout 'devise' @@ -114,13 +115,18 @@ class RegistrationsController < Devise::RegistrationsController def after_sign_up_path_for(user) Gitlab::AppLogger.info(user_created_message(confirmed: user.confirmed?)) - users_sign_up_welcome_path + users_sign_up_welcome_path(glm_tracking_params) end 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) + + # when email confirmation is enabled, path to redirect is saved + # after user confirms and comes back, he will be redirected + store_location_for(:redirect, users_sign_up_welcome_path(glm_tracking_params)) + return identity_verification_redirect_path if custom_confirmation_enabled?(resource) users_almost_there_path(email: resource.email) @@ -183,7 +189,7 @@ class RegistrationsController < Devise::RegistrationsController def resource @resource ||= Users::RegistrationsBuildService - .new(current_user, sign_up_params.merge({ skip_confirmation: skip_email_confirmation? })) + .new(current_user, sign_up_params.merge({ skip_confirmation: registered_with_invite_email? })) .execute end @@ -191,7 +197,7 @@ class RegistrationsController < Devise::RegistrationsController @devise_mapping ||= Devise.mappings[:user] end - def skip_email_confirmation? + def registered_with_invite_email? invite_email = session.delete(:invite_email) sign_up_params[:email] == invite_email diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 9f87ad6aaf6..7d4dd04c6d4 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -25,6 +25,10 @@ class SearchController < ApplicationController end before_action :check_search_rate_limit!, only: search_rate_limited_endpoints + before_action only: :show do + push_frontend_feature_flag(:search_page_vertical_nav, current_user) + end + rescue_from ActiveRecord::QueryCanceled, with: :render_timeout layout 'search' diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index fe3b8d9b8b4..5c969c437f4 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -107,11 +107,11 @@ class SessionsController < Devise::SessionsController end def captcha_enabled? - request.headers[CAPTCHA_HEADER] && Gitlab::Recaptcha.enabled? + request.headers[CAPTCHA_HEADER] && helpers.recaptcha_enabled? end def captcha_on_login_required? - Gitlab::Recaptcha.enabled_on_login? && unverified_anonymous_user? + helpers.recaptcha_enabled_on_login? && unverified_anonymous_user? end # From https://github.com/plataformatec/devise/wiki/How-To:-Use-Recaptcha-with-Devise#devisepasswordscontroller diff --git a/app/controllers/users/namespace_callouts_controller.rb b/app/controllers/users/namespace_callouts_controller.rb deleted file mode 100644 index d4876382dfe..00000000000 --- a/app/controllers/users/namespace_callouts_controller.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -module Users - class NamespaceCalloutsController < Users::CalloutsController - private - - def callout - Users::DismissNamespaceCalloutService.new( - container: nil, current_user: current_user, params: callout_params - ).execute - end - - def callout_params - params.permit(:namespace_id).merge(feature_name: feature_name) - end - end -end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 3c1a3534912..c35aa8e4346 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -35,7 +35,7 @@ class UsersController < ApplicationController feature_category :source_code_management, [:gpg_keys] # TODO: Set higher urgency after resolving https://gitlab.com/gitlab-org/gitlab/-/issues/357914 - urgency :low, [:show, :calendar_activities, :contributed, :activity, :projects, :groups, :calendar] + urgency :low, [:show, :calendar_activities, :contributed, :activity, :projects, :groups, :calendar, :snippets] urgency :default, [:followers, :following, :starred] urgency :high, [:exists] @@ -174,8 +174,9 @@ class UsersController < ApplicationController end def follow - current_user.follow(user) + followee = current_user.follow(user) + flash[:alert] = followee.errors.full_messages.join(', ') if followee&.errors&.any? redirect_path = referer_path(request) || @user redirect_to redirect_path |