Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/controllers')
-rw-r--r--app/controllers/acme_challenges_controller.rb4
-rw-r--r--app/controllers/activity_pub/application_controller.rb2
-rw-r--r--app/controllers/activity_pub/projects/releases_controller.rb47
-rw-r--r--app/controllers/admin/application_settings_controller.rb5
-rw-r--r--app/controllers/admin/clusters_controller.rb2
-rw-r--r--app/controllers/admin/deploy_keys_controller.rb2
-rw-r--r--app/controllers/application_controller.rb32
-rw-r--r--app/controllers/base_action_controller.rb52
-rw-r--r--app/controllers/chaos_controller.rb4
-rw-r--r--app/controllers/clusters/agents/dashboard_controller.rb10
-rw-r--r--app/controllers/clusters/base_controller.rb6
-rw-r--r--app/controllers/clusters/clusters_controller.rb2
-rw-r--r--app/controllers/concerns/autocomplete_sources/expires_in.rb27
-rw-r--r--app/controllers/concerns/import/github_oauth.rb6
-rw-r--r--app/controllers/concerns/membership_actions.rb19
-rw-r--r--app/controllers/concerns/onboarding/redirectable.rb8
-rw-r--r--app/controllers/concerns/product_analytics_tracking.rb6
-rw-r--r--app/controllers/explore/catalog_controller.rb24
-rw-r--r--app/controllers/external_redirect/external_redirect_controller.rb8
-rw-r--r--app/controllers/groups/autocomplete_sources_controller.rb7
-rw-r--r--app/controllers/groups/boards_controller.rb1
-rw-r--r--app/controllers/groups/clusters_controller.rb5
-rw-r--r--app/controllers/groups/work_items_controller.rb2
-rw-r--r--app/controllers/health_controller.rb4
-rw-r--r--app/controllers/ide_controller.rb21
-rw-r--r--app/controllers/import/bulk_imports_controller.rb2
-rw-r--r--app/controllers/invites_controller.rb4
-rw-r--r--app/controllers/jwks_controller.rb4
-rw-r--r--app/controllers/jwt_controller.rb2
-rw-r--r--app/controllers/metrics_controller.rb4
-rw-r--r--app/controllers/oauth/authorized_applications_controller.rb2
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb14
-rw-r--r--app/controllers/organizations/application_controller.rb4
-rw-r--r--app/controllers/organizations/organizations_controller.rb4
-rw-r--r--app/controllers/profiles/active_sessions_controller.rb20
-rw-r--r--app/controllers/profiles/passwords_controller.rb102
-rw-r--r--app/controllers/profiles/personal_access_tokens_controller.rb74
-rw-r--r--app/controllers/profiles/preferences_controller.rb1
-rw-r--r--app/controllers/profiles_controller.rb17
-rw-r--r--app/controllers/projects/autocomplete_sources_controller.rb7
-rw-r--r--app/controllers/projects/blob_controller.rb1
-rw-r--r--app/controllers/projects/boards_controller.rb1
-rw-r--r--app/controllers/projects/ci/pipeline_editor_controller.rb1
-rw-r--r--app/controllers/projects/clusters_controller.rb5
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb41
-rw-r--r--app/controllers/projects/environments_controller.rb4
-rw-r--r--app/controllers/projects/gcp/artifact_registry/base_controller.rb43
-rw-r--r--app/controllers/projects/gcp/artifact_registry/docker_images_controller.rb131
-rw-r--r--app/controllers/projects/gcp/artifact_registry/setup_controller.rb11
-rw-r--r--app/controllers/projects/group_links_controller.rb12
-rw-r--r--app/controllers/projects/integrations/shimos_controller.rb19
-rw-r--r--app/controllers/projects/issues_controller.rb2
-rw-r--r--app/controllers/projects/jobs_controller.rb5
-rw-r--r--app/controllers/projects/merge_requests/diffs_controller.rb6
-rw-r--r--app/controllers/projects/merge_requests_controller.rb53
-rw-r--r--app/controllers/projects/ml/candidates_controller.rb4
-rw-r--r--app/controllers/projects/pipelines_controller.rb10
-rw-r--r--app/controllers/projects/service_desk/custom_email_controller.rb5
-rw-r--r--app/controllers/projects/service_desk_controller.rb9
-rw-r--r--app/controllers/projects/settings/merge_requests_controller.rb1
-rw-r--r--app/controllers/projects/tree_controller.rb1
-rw-r--r--app/controllers/projects_controller.rb15
-rw-r--r--app/controllers/registrations_controller.rb8
-rw-r--r--app/controllers/search_controller.rb42
-rw-r--r--app/controllers/sessions_controller.rb2
-rw-r--r--app/controllers/user_settings/active_sessions_controller.rb22
-rw-r--r--app/controllers/user_settings/application_controller.rb7
-rw-r--r--app/controllers/user_settings/passwords_controller.rb103
-rw-r--r--app/controllers/user_settings/personal_access_tokens_controller.rb76
-rw-r--r--app/controllers/user_settings/user_settings_controller.rb15
-rw-r--r--app/controllers/web_ide/remote_ide_controller.rb2
-rw-r--r--app/controllers/well_known_controller.rb19
72 files changed, 847 insertions, 396 deletions
diff --git a/app/controllers/acme_challenges_controller.rb b/app/controllers/acme_challenges_controller.rb
index 4a7706db94e..a187e43b3df 100644
--- a/app/controllers/acme_challenges_controller.rb
+++ b/app/controllers/acme_challenges_controller.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-# rubocop:disable Rails/ApplicationController
-class AcmeChallengesController < ActionController::Base
+class AcmeChallengesController < BaseActionController
def show
if acme_order
render plain: acme_order.challenge_file_content, content_type: 'text/plain'
@@ -16,4 +15,3 @@ class AcmeChallengesController < ActionController::Base
@acme_order ||= PagesDomainAcmeOrder.find_by_domain_and_token(params[:domain], params[:token])
end
end
-# rubocop:enable Rails/ApplicationController
diff --git a/app/controllers/activity_pub/application_controller.rb b/app/controllers/activity_pub/application_controller.rb
index f9c2b14fe77..70cf881c857 100644
--- a/app/controllers/activity_pub/application_controller.rb
+++ b/app/controllers/activity_pub/application_controller.rb
@@ -8,6 +8,8 @@ module ActivityPub
skip_before_action :authenticate_user!
after_action :set_content_type
+ protect_from_forgery with: :null_session
+
def can?(object, action, subject = :global)
Ability.allowed?(object, action, subject)
end
diff --git a/app/controllers/activity_pub/projects/releases_controller.rb b/app/controllers/activity_pub/projects/releases_controller.rb
index 7c4c2a0322b..eeff96a5ef7 100644
--- a/app/controllers/activity_pub/projects/releases_controller.rb
+++ b/app/controllers/activity_pub/projects/releases_controller.rb
@@ -5,15 +5,27 @@ module ActivityPub
class ReleasesController < ApplicationController
feature_category :release_orchestration
+ before_action :enforce_payload, only: :inbox
+
def index
opts = {
- inbox: nil,
+ inbox: inbox_project_releases_url(@project),
outbox: outbox_project_releases_url(@project)
}
render json: ActivityPub::ReleasesActorSerializer.new.represent(@project, opts)
end
+ def inbox
+ service = inbox_service
+ success = service ? service.execute : true
+
+ response = { success: success }
+ response[:errors] = service.errors unless success
+
+ render json: response
+ end
+
def outbox
serializer = ActivityPub::ReleasesOutboxSerializer.new.with_pagination(request, response)
render json: serializer.represent(releases)
@@ -24,6 +36,39 @@ module ActivityPub
def releases(params = {})
ReleasesFinder.new(@project, current_user, params).execute
end
+
+ def enforce_payload
+ return if payload
+
+ head :unprocessable_entity
+ false
+ end
+
+ def payload
+ @payload ||= begin
+ Gitlab::Json.parse(request.body.read)
+ rescue JSON::ParserError
+ nil
+ end
+ end
+
+ def follow?
+ payload['type'] == 'Follow'
+ end
+
+ def unfollow?
+ undo = payload['type'] == 'Undo'
+ object = payload['object']
+ follow = object.present? && object.is_a?(Hash) && object['type'] == 'Follow'
+ undo && follow
+ end
+
+ def inbox_service
+ return ReleasesFollowService.new(project, payload) if follow?
+ return ReleasesUnfollowService.new(project, payload) if unfollow?
+
+ nil
+ end
end
end
end
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 8cf0ab60fd3..cd099173718 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -164,10 +164,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
params.delete(:domain_denylist_raw) if params[:domain_denylist]
params.delete(:domain_allowlist_raw) if params[:domain_allowlist]
- if params[:application_setting].key?(:user_email_lookup_limit)
- params[:application_setting][:search_rate_limit] ||= params[:application_setting][:user_email_lookup_limit]
- end
-
params[:application_setting].permit(visible_application_setting_attributes)
end
@@ -183,6 +179,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
*::ApplicationSettingsHelper.visible_attributes,
*::ApplicationSettingsHelper.external_authorization_service_attributes,
*ApplicationSetting.kroki_formats_attributes.keys.map { |key| "kroki_formats_#{key}".to_sym },
+ :can_create_organization,
:lets_encrypt_notification_email,
:lets_encrypt_terms_of_service_accepted,
:domain_denylist_file,
diff --git a/app/controllers/admin/clusters_controller.rb b/app/controllers/admin/clusters_controller.rb
index 052c8821588..ead02a7aa8c 100644
--- a/app/controllers/admin/clusters_controller.rb
+++ b/app/controllers/admin/clusters_controller.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class Admin::ClustersController < Clusters::ClustersController
+class Admin::ClustersController < ::Clusters::ClustersController
include EnforcesAdminAuthentication
before_action :ensure_feature_enabled!
diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb
index b24b25446b0..a9b34bf56f6 100644
--- a/app/controllers/admin/deploy_keys_controller.rb
+++ b/app/controllers/admin/deploy_keys_controller.rb
@@ -55,7 +55,7 @@ class Admin::DeployKeysController < Admin::ApplicationController
end
def create_params
- params.require(:deploy_key).permit(:key, :title)
+ params.require(:deploy_key).permit(:key, :title, :expires_at)
end
def update_params
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 6739fc57a1f..fca3bb3460f 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -3,7 +3,7 @@
require 'gon'
require 'fogbugz'
-class ApplicationController < ActionController::Base
+class ApplicationController < BaseActionController
include Gitlab::GonHelper
include Gitlab::NoCacheHeaders
include GitlabRoutingHelper
@@ -25,7 +25,6 @@ class ApplicationController < ActionController::Base
include FlocOptOut
include CheckRateLimit
include RequestPayloadLogger
- extend ContentSecurityPolicyPatch
before_action :limit_session_time, if: -> { !current_user }
before_action :authenticate_user!, except: [:route_not_found]
@@ -113,33 +112,6 @@ class ApplicationController < ActionController::Base
render plain: e.message, status: :service_unavailable
end
- content_security_policy do |p|
- next if p.directives.blank?
-
- if Rails.env.development? && Feature.enabled?(:vite)
- vite_host = ViteRuby.instance.config.host
- vite_port = ViteRuby.instance.config.port
- vite_origin = "#{vite_host}:#{vite_port}"
- http_origin = "http://#{vite_origin}"
- ws_origin = "ws://#{vite_origin}"
- wss_origin = "wss://#{vite_origin}"
- gitlab_ws_origin = Gitlab::Utils.append_path(Gitlab.config.gitlab.url, 'vite-dev/')
- http_path = Gitlab::Utils.append_path(http_origin, 'vite-dev/')
-
- connect_sources = p.directives['connect-src']
- p.connect_src(*(Array.wrap(connect_sources) | [ws_origin, wss_origin, http_path]))
-
- worker_sources = p.directives['worker-src']
- p.worker_src(*(Array.wrap(worker_sources) | [gitlab_ws_origin, http_path]))
- end
-
- next unless Gitlab::CurrentSettings.snowplow_enabled? && !Gitlab::CurrentSettings.snowplow_collector_hostname.blank?
-
- default_connect_src = p.directives['connect-src'] || p.directives['default-src']
- connect_src_values = Array.wrap(default_connect_src) | [Gitlab::CurrentSettings.snowplow_collector_hostname]
- p.connect_src(*connect_src_values)
- end
-
def redirect_back_or_default(default: root_path, options: {})
redirect_back(fallback_location: default, **options)
end
@@ -325,7 +297,7 @@ class ApplicationController < ActionController::Base
return if current_user.nil?
if current_user.password_expired? && current_user.allow_password_authentication?
- redirect_to new_profile_password_path
+ redirect_to new_user_settings_password_path
end
end
diff --git a/app/controllers/base_action_controller.rb b/app/controllers/base_action_controller.rb
new file mode 100644
index 00000000000..e251c44f63d
--- /dev/null
+++ b/app/controllers/base_action_controller.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+# GitLab lightweight base action controller
+#
+# This class should be limited to content that
+# is desired/required for *all* controllers in
+# GitLab.
+#
+# Most controllers inherit from `ApplicationController`.
+# Some controllers don't want or need all of that
+# logic and instead inherit from `ActionController::Base`.
+# This makes it difficult to set security headers and
+# handle other critical logic across *all* controllers.
+#
+# Between this controller and `ApplicationController`
+# no controller should ever inherit directly from
+# `ActionController::Base`
+#
+# rubocop:disable Rails/ApplicationController -- This class is specifically meant as a base class for controllers that
+# don't inherit from ApplicationController
+# rubocop:disable Gitlab/NamespacedClass -- Base controllers live in the global namespace
+class BaseActionController < ActionController::Base
+ extend ContentSecurityPolicyPatch
+
+ content_security_policy do |p|
+ next if p.directives.blank?
+
+ if helpers.vite_enabled?
+ vite_port = ViteRuby.instance.config.port
+ vite_origin = "#{Gitlab.config.gitlab.host}:#{vite_port}"
+ http_origin = "http://#{vite_origin}"
+ ws_origin = "ws://#{vite_origin}"
+ wss_origin = "wss://#{vite_origin}"
+ gitlab_ws_origin = Gitlab::Utils.append_path(Gitlab.config.gitlab.url, 'vite-dev/')
+ http_path = Gitlab::Utils.append_path(http_origin, 'vite-dev/')
+
+ connect_sources = p.directives['connect-src']
+ p.connect_src(*(Array.wrap(connect_sources) | [ws_origin, wss_origin, http_path]))
+
+ worker_sources = p.directives['worker-src']
+ p.worker_src(*(Array.wrap(worker_sources) | [gitlab_ws_origin, http_path]))
+ end
+
+ next unless Gitlab::CurrentSettings.snowplow_enabled? && !Gitlab::CurrentSettings.snowplow_collector_hostname.blank?
+
+ default_connect_src = p.directives['connect-src'] || p.directives['default-src']
+ connect_src_values = Array.wrap(default_connect_src) | [Gitlab::CurrentSettings.snowplow_collector_hostname]
+ p.connect_src(*connect_src_values)
+ end
+end
+# rubocop:enable Gitlab/NamespacedClass
+# rubocop:enable Rails/ApplicationController
diff --git a/app/controllers/chaos_controller.rb b/app/controllers/chaos_controller.rb
index 7328b793b09..b61a8c5ff12 100644
--- a/app/controllers/chaos_controller.rb
+++ b/app/controllers/chaos_controller.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-# rubocop:disable Rails/ApplicationController
-class ChaosController < ActionController::Base
+class ChaosController < BaseActionController
before_action :validate_chaos_secret, unless: :development_or_test?
def leakmem
@@ -95,4 +94,3 @@ class ChaosController < ActionController::Base
Rails.env.development? || Rails.env.test?
end
end
-# rubocop:enable Rails/ApplicationController
diff --git a/app/controllers/clusters/agents/dashboard_controller.rb b/app/controllers/clusters/agents/dashboard_controller.rb
index 1f72aaa4775..7016ebacfba 100644
--- a/app/controllers/clusters/agents/dashboard_controller.rb
+++ b/app/controllers/clusters/agents/dashboard_controller.rb
@@ -6,15 +6,15 @@ module Clusters
include KasCookie
before_action :check_feature_flag!
- before_action :find_agent
- before_action :authorize_read_cluster_agent!
+ before_action :find_agent, only: [:show], if: -> { current_user }
+ before_action :authorize_read_cluster_agent!, only: [:show], if: -> { current_user }
before_action :set_kas_cookie, only: [:show], if: -> { current_user }
feature_category :deployment_management
- def show
- head :ok
- end
+ def index; end
+
+ def show; end
private
diff --git a/app/controllers/clusters/base_controller.rb b/app/controllers/clusters/base_controller.rb
index e7b76b87ad9..495b6463383 100644
--- a/app/controllers/clusters/base_controller.rb
+++ b/app/controllers/clusters/base_controller.rb
@@ -4,6 +4,7 @@ class Clusters::BaseController < ApplicationController
include RoutableActions
skip_before_action :authenticate_user!
+ before_action :clusterable
before_action :authorize_admin_cluster!, except: [:show, :index, :new, :authorize_aws_role, :update]
helper_method :clusterable
@@ -41,6 +42,11 @@ class Clusters::BaseController < ApplicationController
access_denied! unless can?(current_user, :read_prometheus, clusterable)
end
+ # For Group/Clusters and Project/Clusters, the clusterable object (group or project)
+ # is fetched through `find_routable!`, which calls a `render_404` if the user does not have access to the object
+ # The `clusterable` method will need to be in its own before_action call before the `authorize_*` calls
+ # so that the call stack will not proceed to the `authorize_*` calls
+ # and instead just render a not found page after the `clusterable` call
def clusterable
raise NotImplementedError
end
diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb
index b012a4e003e..917007144fa 100644
--- a/app/controllers/clusters/clusters_controller.rb
+++ b/app/controllers/clusters/clusters_controller.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class Clusters::ClustersController < Clusters::BaseController
+class Clusters::ClustersController < ::Clusters::BaseController
include RoutableActions
before_action :cluster, only: [:cluster_status, :show, :update, :destroy, :clear_cache]
diff --git a/app/controllers/concerns/autocomplete_sources/expires_in.rb b/app/controllers/concerns/autocomplete_sources/expires_in.rb
new file mode 100644
index 00000000000..d61cd88bd91
--- /dev/null
+++ b/app/controllers/concerns/autocomplete_sources/expires_in.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module AutocompleteSources
+ module ExpiresIn
+ extend ActiveSupport::Concern
+
+ AUTOCOMPLETE_EXPIRES_IN = 3.minutes
+ AUTOCOMPLETE_CACHED_ACTIONS = [:members, :commands, :labels].freeze
+
+ included do
+ before_action :set_expires_in, only: AUTOCOMPLETE_CACHED_ACTIONS
+ end
+
+ private
+
+ def set_expires_in
+ case action_name.to_sym
+ when :members
+ expires_in AUTOCOMPLETE_EXPIRES_IN if Feature.enabled?(:cache_autocomplete_sources_members, current_user)
+ when :commands
+ expires_in AUTOCOMPLETE_EXPIRES_IN if Feature.enabled?(:cache_autocomplete_sources_commands, current_user)
+ when :labels
+ expires_in AUTOCOMPLETE_EXPIRES_IN if Feature.enabled?(:cache_autocomplete_sources_labels, current_user)
+ end
+ end
+ end
+end
diff --git a/app/controllers/concerns/import/github_oauth.rb b/app/controllers/concerns/import/github_oauth.rb
index ae5a0401155..fa6162254dc 100644
--- a/app/controllers/concerns/import/github_oauth.rb
+++ b/app/controllers/concerns/import/github_oauth.rb
@@ -56,7 +56,11 @@ module Import
session[:auth_on_failure_path] = "#{new_project_path}#import_project"
oauth_client.auth_code.authorize_url(
redirect_uri: callback_import_url,
- scope: 'repo, user, user:email',
+ # read:org only required for collaborator import, which is optional,
+ # but at the time of this OAuth request we do not know which optional
+ # configuration the user will select because the options are only shown
+ # after authenticating
+ scope: 'repo, read:org',
state: state
)
end
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index b4f5589a059..042adc8479e 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -4,6 +4,11 @@ module MembershipActions
include MembersPresentation
extend ActiveSupport::Concern
+ included do
+ before_action :authenticate_user!, only: :request_access
+ before_action :already_a_member!, only: :request_access
+ end
+
def update
member = members_and_requesters.find(params[:id])
result = Members::UpdateService
@@ -166,6 +171,20 @@ module MembershipActions
end
end
end
+
+ def authenticate_user!
+ return if current_user
+
+ redirect_to new_user_session_path
+ end
+
+ def already_a_member!
+ member = members_and_requesters.find_by(user_id: current_user.id) # rubocop: disable CodeReuse/ActiveRecord
+ return if member.nil?
+
+ message = member.request? ? _('You have already requested access.') : _('You already have access.')
+ redirect_to polymorphic_path(membershipable), notice: message
+ end
end
MembershipActions.prepend_mod_with('MembershipActions')
diff --git a/app/controllers/concerns/onboarding/redirectable.rb b/app/controllers/concerns/onboarding/redirectable.rb
index 7e669db9199..15c1847ebe4 100644
--- a/app/controllers/concerns/onboarding/redirectable.rb
+++ b/app/controllers/concerns/onboarding/redirectable.rb
@@ -9,7 +9,7 @@ module Onboarding
def after_sign_up_path
if onboarding_status.single_invite?
flash[:notice] = helpers.invite_accepted_notice(onboarding_status.last_invited_member)
- onboarding_status.last_invited_member_source.activity_path
+ polymorphic_path(onboarding_status.last_invited_member_source)
else
# Invites will come here if there is more than 1.
path_for_signed_in_user
@@ -17,13 +17,13 @@ module Onboarding
end
def path_for_signed_in_user
- stored_location_for(:user) || last_member_activity_path
+ stored_location_for(:user) || last_member_source_path
end
- def last_member_activity_path
+ def last_member_source_path
return dashboard_projects_path unless onboarding_status.last_invited_member_source.present?
- onboarding_status.last_invited_member_source.activity_path
+ polymorphic_path(onboarding_status.last_invited_member_source)
end
end
end
diff --git a/app/controllers/concerns/product_analytics_tracking.rb b/app/controllers/concerns/product_analytics_tracking.rb
index d4610267897..e148f5d063a 100644
--- a/app/controllers/concerns/product_analytics_tracking.rb
+++ b/app/controllers/concerns/product_analytics_tracking.rb
@@ -14,7 +14,7 @@ module ProductAnalyticsTracking
end
def track_internal_event(*controller_actions, name:, conditions: nil)
- custom_conditions = [:trackable_html_request?, :authenticated?, *conditions]
+ custom_conditions = [:trackable_html_request?, *conditions]
after_action only: controller_actions, if: custom_conditions do
Gitlab::InternalEvents.track_event(
@@ -70,8 +70,4 @@ module ProductAnalyticsTracking
cookies[:visitor_id] = { value: uuid, expires: 24.months }
uuid
end
-
- def authenticated?
- current_user.present?
- end
end
diff --git a/app/controllers/explore/catalog_controller.rb b/app/controllers/explore/catalog_controller.rb
index 3cd3771129e..d384ad10c86 100644
--- a/app/controllers/explore/catalog_controller.rb
+++ b/app/controllers/explore/catalog_controller.rb
@@ -2,8 +2,14 @@
module Explore
class CatalogController < Explore::ApplicationController
+ include ProductAnalyticsTracking
+
feature_category :pipeline_composition
- before_action :check_feature_flag
+ before_action :check_resource_access, only: :show
+ track_internal_event :index, name: 'unique_users_visiting_ci_catalog'
+ before_action do
+ push_frontend_feature_flag(:ci_catalog_components_tab, current_user)
+ end
def show; end
@@ -13,8 +19,20 @@ module Explore
private
- def check_feature_flag
- render_404 unless Feature.enabled?(:global_ci_catalog, current_user)
+ def check_resource_access
+ render_404 unless catalog_resource.present?
+ end
+
+ def catalog_resource
+ ::Ci::Catalog::Listing.new(current_user).find_resource(full_path: params[:full_path])
+ end
+
+ def tracking_namespace_source
+ current_user.namespace
+ end
+
+ def tracking_project_source
+ nil
end
end
end
diff --git a/app/controllers/external_redirect/external_redirect_controller.rb b/app/controllers/external_redirect/external_redirect_controller.rb
index 532196157b7..8d5f07ad3eb 100644
--- a/app/controllers/external_redirect/external_redirect_controller.rb
+++ b/app/controllers/external_redirect/external_redirect_controller.rb
@@ -11,7 +11,6 @@ module ExternalRedirect
redirect_to url_param
else
render layout: 'fullscreen', locals: {
- minimal: true,
url: url_param
}
end
@@ -29,8 +28,13 @@ module ExternalRedirect
uri_data.site == Gitlab.config.gitlab.url
end
+ def should_handle_url?(url)
+ # note: To avoid lots of redirects, don't allow url to point to self.
+ ::Gitlab::UrlSanitizer.valid_web?(url) && !url.starts_with?(request.base_url + request.path)
+ end
+
def check_url_param
- render_404 unless ::Gitlab::UrlSanitizer.valid_web?(url_param)
+ render_404 unless should_handle_url?(url_param)
end
end
end
diff --git a/app/controllers/groups/autocomplete_sources_controller.rb b/app/controllers/groups/autocomplete_sources_controller.rb
index 86bf65f4723..7a490b34511 100644
--- a/app/controllers/groups/autocomplete_sources_controller.rb
+++ b/app/controllers/groups/autocomplete_sources_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Groups::AutocompleteSourcesController < Groups::ApplicationController
+ include AutocompleteSources::ExpiresIn
+
feature_category :groups_and_projects, [:members]
feature_category :team_planning, [:issues, :labels, :milestones, :commands]
feature_category :code_review_workflow, [:merge_requests]
@@ -8,11 +10,6 @@ class Groups::AutocompleteSourcesController < Groups::ApplicationController
urgency :low, [:issues, :labels, :milestones, :commands, :merge_requests, :members]
def members
- if Feature.enabled?(:cache_autocomplete_sources_members, current_user)
- # Cache the response on the frontend
- expires_in 3.minutes
- end
-
render json: ::Groups::ParticipantsService.new(@group, current_user).execute(target)
end
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index 6bb807be1c4..7cc0e6a8558 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -8,6 +8,7 @@ class Groups::BoardsController < Groups::ApplicationController
before_action do
push_frontend_feature_flag(:board_multi_select, group)
push_frontend_feature_flag(:apollo_boards, group)
+ push_frontend_feature_flag(:display_work_item_epic_issue_sidebar, group)
experiment(:prominent_create_board_btn, subject: current_user) do |e|
e.control {}
e.candidate {}
diff --git a/app/controllers/groups/clusters_controller.rb b/app/controllers/groups/clusters_controller.rb
index 2fe9faa252f..5b1508f44a2 100644
--- a/app/controllers/groups/clusters_controller.rb
+++ b/app/controllers/groups/clusters_controller.rb
@@ -1,10 +1,9 @@
# frozen_string_literal: true
-class Groups::ClustersController < Clusters::ClustersController
+class Groups::ClustersController < ::Clusters::ClustersController
include ControllerWithCrossProjectAccessCheck
before_action :ensure_feature_enabled!
- prepend_before_action :group
requires_cross_project_access
layout 'group'
@@ -12,7 +11,7 @@ class Groups::ClustersController < Clusters::ClustersController
private
def clusterable
- @clusterable ||= ClusterablePresenter.fabricate(group, current_user: current_user)
+ @clusterable ||= group && ClusterablePresenter.fabricate(group, current_user: current_user)
end
def group
diff --git a/app/controllers/groups/work_items_controller.rb b/app/controllers/groups/work_items_controller.rb
index ece279da778..bfb5b74d2a5 100644
--- a/app/controllers/groups/work_items_controller.rb
+++ b/app/controllers/groups/work_items_controller.rb
@@ -20,3 +20,5 @@ module Groups
end
end
end
+
+Groups::WorkItemsController.prepend_mod
diff --git a/app/controllers/health_controller.rb b/app/controllers/health_controller.rb
index 1381999ab4c..2b2db2f950c 100644
--- a/app/controllers/health_controller.rb
+++ b/app/controllers/health_controller.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-# rubocop:disable Rails/ApplicationController
-class HealthController < ActionController::Base
+class HealthController < BaseActionController
protect_from_forgery with: :exception, prepend: true
include RequiresAllowlistedMonitoringClient
@@ -40,4 +39,3 @@ class HealthController < ActionController::Base
render json: result.json, status: result.http_status
end
end
-# rubocop:enable Rails/ApplicationController
diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb
index 4cc943ac252..4a4d41f3e6f 100644
--- a/app/controllers/ide_controller.rb
+++ b/app/controllers/ide_controller.rb
@@ -5,7 +5,8 @@ class IdeController < ApplicationController
include StaticObjectExternalStorageCSP
include Gitlab::Utils::StrongMemoize
- before_action :authorize_read_project!
+ before_action :authorize_read_project!, only: [:index]
+ before_action :ensure_web_ide_oauth_application!, only: [:index]
before_action do
push_frontend_feature_flag(:build_service_proxy)
@@ -24,7 +25,17 @@ class IdeController < ApplicationController
@fork_info = fork_info(project, params[:branch])
end
- render layout: 'fullscreen', locals: { minimal: helpers.use_new_web_ide? }
+ render layout: helpers.use_new_web_ide? ? 'fullscreen' : 'application'
+ end
+
+ def oauth_redirect
+ return render_404 unless ::Gitlab::WebIde::DefaultOauthApplication.feature_enabled?(current_user)
+ # TODO - It's **possible** we end up here and no oauth application has been set up.
+ # We need to have better handling of these edge cases. Here's a follow-up issue:
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/433322
+ return render_404 unless ::Gitlab::WebIde::DefaultOauthApplication.oauth_application
+
+ render layout: 'fullscreen', locals: { minimal: true }
end
private
@@ -33,6 +44,12 @@ class IdeController < ApplicationController
render_404 unless can?(current_user, :read_project, project)
end
+ def ensure_web_ide_oauth_application!
+ return unless ::Gitlab::WebIde::DefaultOauthApplication.feature_enabled?(current_user)
+
+ ::Gitlab::WebIde::DefaultOauthApplication.ensure_oauth_application!
+ end
+
def fork_info(project, branch)
return if can?(current_user, :push_code, project)
diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb
index bc425323d6f..e211ea70a56 100644
--- a/app/controllers/import/bulk_imports_controller.rb
+++ b/app/controllers/import/bulk_imports_controller.rb
@@ -76,7 +76,7 @@ class Import::BulkImportsController < ApplicationController
def realtime_changes
Gitlab::PollingInterval.set_header(response, interval: POLLING_INTERVAL)
- render json: current_user_bulk_imports.to_json(only: [:id], methods: [:status_name])
+ render json: current_user_bulk_imports.to_json(only: [:id], methods: [:status_name, :has_failures])
end
private
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index c058329680a..fcd87f46f67 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -125,14 +125,14 @@ class InvitesController < ApplicationController
name: member.source.full_name,
url: project_url(member.source),
title: _("project"),
- path: member.source.activity_path
+ path: project_path(member.source)
}
when Group
{
name: member.source.name,
url: group_url(member.source),
title: _("group"),
- path: member.source.activity_path
+ path: group_path(member.source)
}
end
end
diff --git a/app/controllers/jwks_controller.rb b/app/controllers/jwks_controller.rb
index d3a8d3dafea..2e030cf46c4 100644
--- a/app/controllers/jwks_controller.rb
+++ b/app/controllers/jwks_controller.rb
@@ -2,6 +2,10 @@
class JwksController < Doorkeeper::OpenidConnect::DiscoveryController
def index
+ if ::Feature.enabled?(:cache_control_headers_for_openid_jwks)
+ expires_in 24.hours, public: true, must_revalidate: true, 'no-transform': true
+ end
+
render json: { keys: payload }
end
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb
index 83409c7e096..e6e232cfbc3 100644
--- a/app/controllers/jwt_controller.rb
+++ b/app/controllers/jwt_controller.rb
@@ -33,6 +33,7 @@ class JwtController < ApplicationController
@authentication_result = Gitlab::Auth::Result.new(nil, nil, :none, Gitlab::Auth.read_only_authentication_abilities)
authenticate_with_http_basic do |login, password|
+ @raw_token = password
@authentication_result = Gitlab::Auth.find_for_git_client(login, password, project: nil, request: request)
if @authentication_result.failed?
@@ -80,6 +81,7 @@ class JwtController < ApplicationController
def additional_params
{
scopes: scopes_param,
+ raw_token: @raw_token,
deploy_token: @authentication_result.deploy_token,
auth_type: @authentication_result.type
}.compact
diff --git a/app/controllers/metrics_controller.rb b/app/controllers/metrics_controller.rb
index 9f41c092fa0..61851fd1c60 100644
--- a/app/controllers/metrics_controller.rb
+++ b/app/controllers/metrics_controller.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-# rubocop:disable Rails/ApplicationController
-class MetricsController < ActionController::Base
+class MetricsController < BaseActionController
include RequiresAllowlistedMonitoringClient
protect_from_forgery with: :exception, prepend: true
@@ -36,4 +35,3 @@ class MetricsController < ActionController::Base
)
end
end
-# rubocop:enable Rails/ApplicationController
diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb
index 6fc2eb6bc45..42c65a845c6 100644
--- a/app/controllers/oauth/authorized_applications_controller.rb
+++ b/app/controllers/oauth/authorized_applications_controller.rb
@@ -19,7 +19,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
Doorkeeper::Application.revoke_tokens_and_grants_for(params[:id], current_resource_owner)
end
- redirect_to applications_profile_url,
+ redirect_to user_settings_applications_url,
status: :found,
notice: I18n.t(:notice, scope: [:doorkeeper, :flash, :authorized_applications, :destroy])
end
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index a97516fddff..907ece1a06e 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -8,6 +8,9 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
include KnownSignIn
include AcceptsPendingInvitations
include Onboarding::Redirectable
+ include InternalRedirect
+
+ ACTIVE_SINCE_KEY = 'active_since'
after_action :verify_known_sign_in
@@ -113,14 +116,21 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
super
end
+ def store_redirect_to
+ # overridden in EE
+ end
+
def omniauth_flow(auth_module, identity_linker: nil)
if fragment = request.env.dig('omniauth.params', 'redirect_fragment').presence
store_redirect_fragment(fragment)
end
+ store_redirect_to
+
if current_user
return render_403 unless link_provider_allowed?(oauth['provider'])
+ set_session_active_since(oauth['provider']) if ::AuthHelper.saml_providers.include?(oauth['provider'].to_sym)
track_event(current_user, oauth['provider'], 'succeeded')
if Gitlab::CurrentSettings.admin_mode
@@ -167,6 +177,9 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
end
+ # Overrided in EE
+ def set_session_active_since(id); end
+
def sign_in_user_flow(auth_user_class)
auth_user = build_auth_user(auth_user_class)
new_user = auth_user.new?
@@ -181,6 +194,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
Gitlab::Tracking.event(self.class.name, "#{oauth['provider']}_sso", user: @user) if new_user
set_remember_me(@user)
+ set_session_active_since(oauth['provider']) if ::AuthHelper.saml_providers.include?(oauth['provider'].to_sym)
if @user.two_factor_enabled? && !auth_user.bypass_two_factor?
prompt_for_two_factor(@user)
diff --git a/app/controllers/organizations/application_controller.rb b/app/controllers/organizations/application_controller.rb
index 8a99b6804ae..9cc33ec0447 100644
--- a/app/controllers/organizations/application_controller.rb
+++ b/app/controllers/organizations/application_controller.rb
@@ -28,6 +28,10 @@ module Organizations
access_denied! unless can?(current_user, :read_organization, organization)
end
+ def authorize_read_organization_user!
+ access_denied! unless can?(current_user, :read_organization_user, organization)
+ end
+
def authorize_admin_organization!
access_denied! unless can?(current_user, :admin_organization, organization)
end
diff --git a/app/controllers/organizations/organizations_controller.rb b/app/controllers/organizations/organizations_controller.rb
index 3085f0c07d1..9f09627b1e4 100644
--- a/app/controllers/organizations/organizations_controller.rb
+++ b/app/controllers/organizations/organizations_controller.rb
@@ -4,7 +4,7 @@ module Organizations
class OrganizationsController < ApplicationController
feature_category :cell
- skip_before_action :authenticate_user!, except: [:index, :new]
+ skip_before_action :authenticate_user!, except: [:index, :new, :users]
def index; end
@@ -21,7 +21,7 @@ module Organizations
end
def users
- authorize_read_organization!
+ authorize_read_organization_user!
end
end
end
diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb
deleted file mode 100644
index 5a86179b89f..00000000000
--- a/app/controllers/profiles/active_sessions_controller.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-class Profiles::ActiveSessionsController < Profiles::ApplicationController
- feature_category :system_access
-
- def index
- @sessions = ActiveSession.list(current_user).reject(&:is_impersonated)
- end
-
- def destroy
- # params[:id] can be an Rack::Session::SessionId#private_id
- ActiveSession.destroy_session(current_user, params[:id])
- current_user.forget_me!
-
- respond_to do |format|
- format.html { redirect_to profile_active_sessions_url, status: :found }
- format.js { head :ok }
- end
- end
-end
diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb
deleted file mode 100644
index 7a0dfbbba0d..00000000000
--- a/app/controllers/profiles/passwords_controller.rb
+++ /dev/null
@@ -1,102 +0,0 @@
-# frozen_string_literal: true
-
-class Profiles::PasswordsController < Profiles::ApplicationController
- include Gitlab::Tracking::Helpers::WeakPasswordErrorEvent
-
- skip_before_action :check_password_expiration, only: [:new, :create]
- skip_before_action :check_two_factor_requirement, only: [:new, :create]
-
- before_action :set_user
- before_action :authorize_change_password!
-
- layout :determine_layout
-
- feature_category :system_access
-
- def new
- end
-
- def create
- unless @user.password_automatically_set || @user.valid_password?(user_params[:password])
- redirect_to new_profile_password_path, alert: _('You must provide a valid current password')
- return
- end
-
- result = Users::UpdateService.new(current_user, password_attributes.merge(user: @user)).execute
-
- if result[:status] == :success
- Users::UpdateService.new(current_user, user: @user, password_expires_at: nil).execute
-
- redirect_to root_path, notice: _('Password successfully changed')
- else
- track_weak_password_error(@user, self.class.name, 'create')
- render :new
- end
- end
-
- def edit
- end
-
- def update
- unless @user.password_automatically_set || @user.valid_password?(user_params[:password])
- handle_invalid_current_password_attempt!
-
- redirect_to edit_profile_password_path, alert: _('You must provide a valid current password')
- return
- end
-
- result = Users::UpdateService.new(current_user, password_attributes.merge(user: @user)).execute
-
- if result[:status] == :success
- flash[:notice] = _('Password was successfully updated. Please sign in again.')
- redirect_to new_user_session_path
- else
- track_weak_password_error(@user, self.class.name, 'update')
- @user.reset
- render 'edit'
- end
- end
-
- def reset
- current_user.send_reset_password_instructions
- redirect_to edit_profile_password_path, notice: _('We sent you an email with reset password instructions')
- end
-
- private
-
- def set_user
- @user = current_user
- end
-
- def determine_layout
- if [:new, :create].include?(action_name.to_sym)
- 'application'
- else
- 'profile'
- end
- end
-
- def authorize_change_password!
- render_404 unless @user.allow_password_authentication?
- end
-
- def handle_invalid_current_password_attempt!
- Gitlab::AppLogger.info(message: 'Invalid current password when attempting to update user password', username: @user.username, ip: request.remote_ip)
-
- @user.increment_failed_attempts!
- end
-
- def user_params
- params.require(:user).permit(:password, :new_password, :password_confirmation)
- end
-
- def password_attributes
- {
- password: user_params[:new_password],
- password_confirmation: user_params[:password_confirmation],
- password_automatically_set: false
- }
- end
-end
-
-Profiles::PasswordsController.prepend_mod
diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb
deleted file mode 100644
index 4b6e2f768fa..00000000000
--- a/app/controllers/profiles/personal_access_tokens_controller.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-# frozen_string_literal: true
-
-class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
- include RenderAccessTokens
-
- feature_category :system_access
-
- 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?
- @personal_access_token = finder.build(
- name: params[:name],
- scopes: scopes
- )
-
- respond_to do |format|
- format.html
- format.json do
- render json: @active_access_tokens
- end
- end
- end
-
- def create
- result = ::PersonalAccessTokens::CreateService.new(
- current_user: current_user,
- target_user: current_user,
- params: personal_access_token_params,
- concatenate_errors: false
- ).execute
-
- @personal_access_token = result.payload[:personal_access_token]
-
- if result.success?
- render json: { new_token: @personal_access_token.token,
- active_access_tokens: active_access_tokens }, status: :ok
- else
- render json: { errors: result.errors }, status: :unprocessable_entity
- end
- end
-
- def revoke
- @personal_access_token = finder.find(params[:id])
- service = PersonalAccessTokens::RevokeService.new(current_user, token: @personal_access_token).execute
- service.success? ? flash[:notice] = service.message : flash[:alert] = service.message
-
- redirect_to profile_personal_access_tokens_path
- end
-
- private
-
- def finder(options = {})
- PersonalAccessTokensFinder.new({ user: current_user, impersonation: false }.merge(options))
- end
-
- def personal_access_token_params
- params.require(:personal_access_token).permit(:name, :expires_at, scopes: [])
- end
-
- def set_index_vars
- @scopes = Gitlab::Auth.available_scopes_for(current_user)
- @active_access_tokens = active_access_tokens
- end
-
- def represent(tokens)
- ::PersonalAccessTokenSerializer.new.represent(tokens)
- 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 7059e2a0371..4c93c738484 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -41,6 +41,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController
:color_scheme_id,
:diffs_deletion_color,
:diffs_addition_color,
+ :home_organization_id,
:layout,
:dashboard,
:project_view,
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index cb29f0f3539..39a070b6405 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -14,7 +14,6 @@ class ProfilesController < Profiles::ApplicationController
feature_category :user_profile, [:show, :update, :reset_incoming_email_token, :reset_feed_token,
:reset_static_object_token, :update_username]
- feature_category :system_access, [:audit_log]
urgency :low, [:show, :update]
def show
@@ -43,7 +42,7 @@ class ProfilesController < Profiles::ApplicationController
flash[:notice] = s_("Profiles|Incoming email token was successfully reset")
- redirect_to profile_personal_access_tokens_path
+ redirect_to user_settings_personal_access_tokens_path
end
def reset_feed_token
@@ -53,7 +52,7 @@ class ProfilesController < Profiles::ApplicationController
flash[:notice] = s_('Profiles|Feed token was successfully reset')
- redirect_to profile_personal_access_tokens_path
+ redirect_to user_settings_personal_access_tokens_path
end
def reset_static_object_token
@@ -61,20 +60,10 @@ class ProfilesController < Profiles::ApplicationController
user.reset_static_object_token!
end
- redirect_to profile_personal_access_tokens_path,
+ redirect_to user_settings_personal_access_tokens_path,
notice: s_('Profiles|Static object token was successfully reset')
end
- # rubocop: disable CodeReuse/ActiveRecord
- def audit_log
- @events = AuthenticationEvent.where(user: current_user)
- .order("created_at DESC")
- .page(params[:page])
-
- Gitlab::Tracking.event(self.class.name, 'search_audit_event', user: current_user)
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
def update_username
result = Users::UpdateService.new(current_user, user: @user, username: username_param).execute
diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb
index 60c8fe97e81..ff3484d3020 100644
--- a/app/controllers/projects/autocomplete_sources_controller.rb
+++ b/app/controllers/projects/autocomplete_sources_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Projects::AutocompleteSourcesController < Projects::ApplicationController
+ include AutocompleteSources::ExpiresIn
+
before_action :authorize_read_milestone!, only: :milestones
before_action :authorize_read_crm_contact!, only: :contacts
@@ -13,11 +15,6 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController
urgency :low, [:issues, :labels, :milestones, :commands, :contacts]
def members
- if Feature.enabled?(:cache_autocomplete_sources_members, current_user)
- # Cache the response on the frontend
- expires_in 3.minutes
- end
-
render json: ::Projects::ParticipantsService.new(@project, current_user).execute(target)
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 7371902a6bd..7851e2ac80b 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -52,6 +52,7 @@ class Projects::BlobController < Projects::ApplicationController
push_frontend_feature_flag(:blob_blame_info, @project)
push_frontend_feature_flag(:highlight_js_worker, @project)
push_frontend_feature_flag(:explain_code_chat, current_user)
+ push_frontend_feature_flag(:encoding_logs_tree)
push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks)
end
diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb
index 84872d1e978..fd853b5aaed 100644
--- a/app/controllers/projects/boards_controller.rb
+++ b/app/controllers/projects/boards_controller.rb
@@ -8,6 +8,7 @@ class Projects::BoardsController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:board_multi_select, project)
push_frontend_feature_flag(:apollo_boards, project)
+ push_frontend_feature_flag(:display_work_item_epic_issue_sidebar, project)
experiment(:prominent_create_board_btn, subject: current_user) do |e|
e.control {}
e.candidate {}
diff --git a/app/controllers/projects/ci/pipeline_editor_controller.rb b/app/controllers/projects/ci/pipeline_editor_controller.rb
index 6e7f764c5c1..62a5baccc62 100644
--- a/app/controllers/projects/ci/pipeline_editor_controller.rb
+++ b/app/controllers/projects/ci/pipeline_editor_controller.rb
@@ -4,7 +4,6 @@ class Projects::Ci::PipelineEditorController < Projects::ApplicationController
before_action :check_can_collaborate!
before_action do
push_frontend_feature_flag(:ci_job_assistant_drawer, @project)
- push_frontend_feature_flag(:ai_ci_config_generator, @user)
push_frontend_feature_flag(:ci_graphql_pipeline_mini_graph, @project)
end
diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb
index b781365b3c3..dd969efa49b 100644
--- a/app/controllers/projects/clusters_controller.rb
+++ b/app/controllers/projects/clusters_controller.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
-class Projects::ClustersController < Clusters::ClustersController
- prepend_before_action :project
+class Projects::ClustersController < ::Clusters::ClustersController
before_action :repository
before_action do
@@ -13,7 +12,7 @@ class Projects::ClustersController < Clusters::ClustersController
private
def clusterable
- @clusterable ||= ClusterablePresenter.fabricate(project, current_user: current_user)
+ @clusterable ||= project && ClusterablePresenter.fabricate(project, current_user: current_user)
end
def project
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index 9cdbd2a30f6..66ce501f9f0 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -22,6 +22,34 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
end
+ def enabled_keys
+ respond_to do |format|
+ format.json do
+ enabled_keys = find_keys(filter: :enabled_keys)
+ render json: { keys: serialize(enabled_keys) }
+ end
+ end
+ end
+
+ def available_project_keys
+ respond_to do |format|
+ format.json do
+ available_project_keys = find_keys(filter: :available_project_keys)
+ render json: { keys: serialize(available_project_keys) }
+ end
+ end
+ end
+
+ def available_public_keys
+ respond_to do |format|
+ format.json do
+ available_public_keys = find_keys(filter: :available_public_keys)
+
+ render json: { keys: serialize(available_public_keys) }
+ end
+ end
+ end
+
def new
redirect_to_repository
end
@@ -108,4 +136,17 @@ class Projects::DeployKeysController < Projects::ApplicationController
def redirect_to_repository
redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings')
end
+
+ def find_keys(params)
+ DeployKeys::DeployKeysFinder.new(@project, current_user, params)
+ .execute
+ end
+
+ def serialize(keys)
+ opts = { user: current_user, project: project }
+
+ DeployKeys::DeployKeySerializer.new
+ .with_pagination(request, response)
+ .represent(keys, opts)
+ end
end
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 4b2749dc716..8cdd6efa7c5 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -14,6 +14,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController
push_frontend_feature_flag(:k8s_watch_api, project)
end
+ before_action only: [:folder] do
+ push_frontend_feature_flag(:environments_folder_new_look, project)
+ end
+
before_action :authorize_read_environment!
before_action :authorize_create_environment!, only: [:new, :create]
before_action :authorize_stop_environment!, only: [:stop]
diff --git a/app/controllers/projects/gcp/artifact_registry/base_controller.rb b/app/controllers/projects/gcp/artifact_registry/base_controller.rb
new file mode 100644
index 00000000000..4084427f3e5
--- /dev/null
+++ b/app/controllers/projects/gcp/artifact_registry/base_controller.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Projects
+ module Gcp
+ module ArtifactRegistry
+ class BaseController < ::Projects::ApplicationController
+ before_action :ensure_feature_flag
+ before_action :ensure_saas
+ before_action :authorize_read_container_image!
+ before_action :ensure_private_project
+
+ feature_category :container_registry
+ urgency :low
+
+ private
+
+ def ensure_feature_flag
+ return if Feature.enabled?(:gcp_technical_demo, project)
+
+ @error = 'Feature flag disabled'
+
+ render
+ end
+
+ def ensure_saas
+ return if Gitlab.com_except_jh? # rubocop: disable Gitlab/AvoidGitlabInstanceChecks -- demo requirement
+
+ @error = "Can't run here"
+
+ render
+ end
+
+ def ensure_private_project
+ return if project.private?
+
+ @error = 'Can only run on private projects'
+
+ render
+ end
+ end
+ end
+ end
+end
diff --git a/app/controllers/projects/gcp/artifact_registry/docker_images_controller.rb b/app/controllers/projects/gcp/artifact_registry/docker_images_controller.rb
new file mode 100644
index 00000000000..b88b86975a4
--- /dev/null
+++ b/app/controllers/projects/gcp/artifact_registry/docker_images_controller.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+module Projects
+ module Gcp
+ module ArtifactRegistry
+ class DockerImagesController < Projects::Gcp::ArtifactRegistry::BaseController
+ before_action :require_gcp_params
+ before_action :handle_pagination
+
+ REPO_NAME_REGEX = %r{/repositories/(.*)/dockerImages/}
+
+ def index
+ result = service.execute(page_token: params[:page_token])
+
+ if result.success?
+ @docker_images = process_docker_images(result.payload[:images] || [])
+ @next_page_token = result.payload[:next_page_token]
+ @artifact_repository_name = artifact_repository_name
+ @error = @docker_images.blank? ? 'No docker images' : false
+ else
+ @error = result.message
+ end
+ end
+
+ private
+
+ def service
+ ::Integrations::GoogleCloudPlatform::ArtifactRegistry::ListDockerImagesService.new(
+ project: @project,
+ current_user: current_user,
+ params: {
+ gcp_project_id: gcp_project_id,
+ gcp_location: gcp_location,
+ gcp_repository: gcp_ar_repository,
+ gcp_wlif: gcp_wlif_url
+ }
+ )
+ end
+
+ def process_docker_images(raw_images)
+ raw_images.map { |r| process_docker_image(r) }
+ end
+
+ def process_docker_image(raw_image)
+ DockerImage.new(
+ name: raw_image[:name],
+ uri: raw_image[:uri],
+ tags: raw_image[:tags],
+ image_size_bytes: raw_image[:size_bytes],
+ media_type: raw_image[:media_type],
+ upload_time: raw_image[:uploaded_at],
+ build_time: raw_image[:built_at],
+ update_time: raw_image[:updated_at]
+ )
+ end
+
+ def artifact_repository_name
+ return unless @docker_images.present?
+
+ (@docker_images.first.name || '')[REPO_NAME_REGEX, 1]
+ end
+
+ def handle_pagination
+ @page = Integer(params[:page] || 1)
+ @page_tokens = {}
+ @previous_page_token = nil
+
+ if params[:page_tokens]
+ @page_tokens = ::Gitlab::Json.parse(Base64.decode64(params[:page_tokens]))
+ @previous_page_token = @page_tokens[(@page - 1).to_s]
+ end
+
+ @page_tokens[@page.to_s] = params[:page_token]
+ @page_tokens = Base64.encode64(::Gitlab::Json.dump(@page_tokens.compact))
+ end
+
+ def require_gcp_params
+ return unless gcp_project_id.blank? || gcp_location.blank? || gcp_ar_repository.blank? || gcp_wlif_url.blank?
+
+ redirect_to new_namespace_project_gcp_artifact_registry_setup_path
+ end
+
+ def gcp_project_id
+ params[:gcp_project_id]
+ end
+
+ def gcp_location
+ params[:gcp_location]
+ end
+
+ def gcp_ar_repository
+ params[:gcp_ar_repository]
+ end
+
+ def gcp_wlif_url
+ params[:gcp_wlif_url]
+ end
+
+ class DockerImage
+ include ActiveModel::API
+
+ attr_accessor :name, :uri, :tags, :image_size_bytes, :upload_time, :media_type, :build_time, :update_time
+
+ SHORT_NAME_REGEX = %r{dockerImages/(.*)$}
+
+ def short_name
+ (name || '')[SHORT_NAME_REGEX, 1]
+ end
+
+ def updated_at
+ return unless update_time
+
+ Time.zone.parse(update_time)
+ end
+
+ def built_at
+ return unless build_time
+
+ Time.zone.parse(build_time)
+ end
+
+ def uploaded_at
+ return unless upload_time
+
+ Time.zone.parse(upload_time)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/controllers/projects/gcp/artifact_registry/setup_controller.rb b/app/controllers/projects/gcp/artifact_registry/setup_controller.rb
new file mode 100644
index 00000000000..e90304ce593
--- /dev/null
+++ b/app/controllers/projects/gcp/artifact_registry/setup_controller.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Projects
+ module Gcp
+ module ArtifactRegistry
+ class SetupController < ::Projects::Gcp::ArtifactRegistry::BaseController
+ def new; end
+ end
+ end
+ end
+end
diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb
index 5f8bf423219..855b9824cf2 100644
--- a/app/controllers/projects/group_links_controller.rb
+++ b/app/controllers/projects/group_links_controller.rb
@@ -3,7 +3,7 @@
class Projects::GroupLinksController < Projects::ApplicationController
layout 'project_settings'
before_action :authorize_admin_project!, except: [:destroy]
- before_action :authorize_admin_project_group_link!, only: [:destroy]
+ before_action :authorize_manage_destroy!, only: [:destroy]
before_action :authorize_admin_project_member!, only: [:update]
feature_category :groups_and_projects
@@ -20,8 +20,8 @@ class Projects::GroupLinksController < Projects::ApplicationController
else
render json: {}
end
- elsif result.reason == :not_found
- render json: { message: result.message }, status: :not_found
+ else
+ render json: { message: result.message }, status: result.reason
end
end
@@ -47,7 +47,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
end
format.js do
- render json: { message: result.message }, status: :not_found if result.reason == :not_found
+ render json: { message: result.message }, status: result.reason
end
end
end
@@ -55,8 +55,8 @@ class Projects::GroupLinksController < Projects::ApplicationController
protected
- def authorize_admin_project_group_link!
- render_404 unless can?(current_user, :admin_project_group_link, group_link)
+ def authorize_manage_destroy!
+ render_404 unless can?(current_user, :manage_destroy, group_link)
end
def group_link
diff --git a/app/controllers/projects/integrations/shimos_controller.rb b/app/controllers/projects/integrations/shimos_controller.rb
deleted file mode 100644
index 6c8313d0805..00000000000
--- a/app/controllers/projects/integrations/shimos_controller.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: true
-
-module Projects
- module Integrations
- class ShimosController < Projects::ApplicationController
- feature_category :integrations
-
- before_action :ensure_renderable
-
- def show; end
-
- private
-
- def ensure_renderable
- render_404 unless project.has_shimo? && project.shimo_integration&.render?
- end
- end
- end
-end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index a6444dc038c..d0eabf8d837 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -49,6 +49,7 @@ class Projects::IssuesController < Projects::ApplicationController
push_frontend_feature_flag(:service_desk_ticket)
push_frontend_feature_flag(:issues_list_drawer, project)
push_frontend_feature_flag(:linked_work_items, project)
+ push_frontend_feature_flag(:display_work_item_epic_issue_sidebar, project)
end
before_action only: [:index, :show] do
@@ -67,6 +68,7 @@ class Projects::IssuesController < Projects::ApplicationController
push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?)
push_frontend_feature_flag(:epic_widget_edit_confirmation, project)
push_frontend_feature_flag(:moved_mr_sidebar, project)
+ push_frontend_feature_flag(:display_work_item_epic_issue_sidebar, project)
push_force_frontend_feature_flag(:linked_work_items, project.linked_work_items_feature_flag_enabled?)
push_frontend_feature_flag(:notifications_todos_buttons, current_user)
end
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index d5a7f25d4ce..4062e625e07 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -21,7 +21,6 @@ class Projects::JobsController < Projects::ApplicationController
before_action :verify_api_request!, only: :terminal_websocket_authorize
before_action :authorize_create_proxy_build!, only: :proxy_websocket_authorize
before_action :verify_proxy_request!, only: :proxy_websocket_authorize
- before_action :push_job_log_jump_to_failures, only: [:show]
before_action :reject_if_build_artifacts_size_refreshing!, only: [:erase]
before_action :push_ai_build_failure_cause, only: [:show]
layout 'project'
@@ -277,10 +276,6 @@ class Projects::JobsController < Projects::ApplicationController
::Gitlab::Workhorse.channel_websocket(service)
end
- def push_job_log_jump_to_failures
- push_frontend_feature_flag(:job_log_jump_to_failures, @project)
- end
-
def push_ai_build_failure_cause
push_frontend_feature_flag(:ai_build_failure_cause, @project)
end
diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb
index 5bd0063ab95..b269d41fa77 100644
--- a/app/controllers/projects/merge_requests/diffs_controller.rb
+++ b/app/controllers/projects/merge_requests/diffs_controller.rb
@@ -32,8 +32,11 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
# rubocop: disable Metrics/AbcSize
def diffs_batch
+ collapse_generated = Feature.enabled?(:collapse_generated_diff_files, project)
+
diff_options_hash = diff_options
diff_options_hash[:paths] = params[:paths] if params[:paths]
+ diff_options_hash[:collapse_generated] = collapse_generated
diffs = @compare.diffs_in_batch(params[:page], params[:per_page], diff_options: diff_options_hash)
@@ -59,7 +62,8 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
params[:expanded],
params[:page],
params[:per_page],
- options[:merge_ref_head_diff]
+ options[:merge_ref_head_diff],
+ collapse_generated
]
expires_in(1.day) if cache_with_max_age?
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index eb7505bd81f..0899e303305 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -41,18 +41,11 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:moved_mr_sidebar, project)
push_frontend_feature_flag(:sast_reports_in_inline_diff, project)
push_frontend_feature_flag(:mr_experience_survey, project)
- push_force_frontend_feature_flag(:summarize_my_code_review, summarize_my_code_review_enabled?)
push_frontend_feature_flag(:ci_job_failures_in_mr, project)
push_frontend_feature_flag(:mr_pipelines_graphql, project)
push_frontend_feature_flag(:notifications_todos_buttons, current_user)
- push_frontend_feature_flag(:widget_pipeline_pass_subscription_update, project)
push_frontend_feature_flag(:mr_request_changes, current_user)
- end
-
- before_action only: [:edit] do
- if can?(current_user, :fill_in_merge_request_template, project)
- push_frontend_feature_flag(:fill_in_mr_template, project)
- end
+ push_frontend_feature_flag(:merge_blocked_component, current_user)
end
around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :diffs, :discussions]
@@ -345,15 +338,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
def discussions
- if Feature.enabled?(:only_highlight_discussions_requested, project)
- super do |discussion_notes|
- note_ids = discussion_notes.flat_map { |x| x.notes.collect(&:id) }
- merge_request.discussions_diffs.load_highlight(diff_note_ids: note_ids)
- end
- else
- merge_request.discussions_diffs.load_highlight
-
- super
+ super do |discussion_notes|
+ note_ids = discussion_notes.flat_map { |x| x.notes.collect(&:id) }
+ merge_request.discussions_diffs.load_highlight(diff_note_ids: note_ids)
end
end
@@ -461,7 +448,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
@update_current_user_path = expose_path(api_v4_user_preferences_path)
@endpoint_metadata_url = endpoint_metadata_url(@project, @merge_request)
@endpoint_diff_batch_url = endpoint_diff_batch_url(@project, @merge_request)
- @diffs_batch_cache_key = @merge_request.merge_head_diff&.id if merge_request.diffs_batch_cache_with_max_age?
+
+ if merge_request.diffs_batch_cache_with_max_age?
+ @diffs_batch_cache_key = @merge_request.merge_head_diff&.patch_id_sha
+ end
set_pipeline_variables
@@ -471,11 +461,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
def get_diffs_count
- if show_only_context_commits?
- @merge_request.context_commits_diff.raw_diffs.size
- else
- @merge_request.diff_size
- end
+ return @merge_request.context_commits_diff.raw_diffs.size if show_only_context_commits?
+ return @merge_request.merge_request_diffs.find_by_id(params[:diff_id])&.size if params[:diff_id]
+
+ @merge_request.diff_size
end
def merge_request_update_params
@@ -598,7 +587,11 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
when :parsed
render json: Gitlab::Json.dump(report_comparison[:data]), status: :ok
when :error
- render json: { status_reason: report_comparison[:status_reason] }, status: :bad_request
+ render json: {
+ errors: [report_comparison[:status_reason]],
+ status_reason: report_comparison[:status_reason]
+ },
+ status: :bad_request
else
raise "Failed to build comparison response as comparison yielded unknown status '#{report_comparison[:status]}'"
end
@@ -629,7 +622,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
params = request
.query_parameters
.merge(view: 'inline', diff_head: true, w: show_whitespace, page: '0', per_page: per_page)
- params[:ck] = merge_request.merge_head_diff&.id if merge_request.diffs_batch_cache_with_max_age?
+ params[:ck] = merge_request.merge_head_diff&.patch_id_sha if merge_request.diffs_batch_cache_with_max_age?
diffs_batch_project_json_merge_request_path(project, merge_request, 'json', params)
end
@@ -638,16 +631,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
Date.strptime(date, "%Y-%m-%d")&.to_time&.to_i if date
rescue Date::Error, TypeError
end
-
- def summarize_my_code_review_enabled?
- namespace = project&.group&.root_ancestor
- return false if namespace.nil?
-
- Feature.enabled?(:summarize_my_code_review, current_user) &&
- namespace.group_namespace? &&
- namespace.licensed_feature_available?(:summarize_my_mr_code_review) &&
- Gitlab::Llm::StageCheck.available?(namespace, :summarize_my_mr_code_review)
- end
end
Projects::MergeRequestsController.prepend_mod_with('Projects::MergeRequestsController')
diff --git a/app/controllers/projects/ml/candidates_controller.rb b/app/controllers/projects/ml/candidates_controller.rb
index 12111c45fde..9905e454acb 100644
--- a/app/controllers/projects/ml/candidates_controller.rb
+++ b/app/controllers/projects/ml/candidates_controller.rb
@@ -9,9 +9,7 @@ module Projects
feature_category :mlops
- def show
- @include_ci_info = @candidate.from_ci? && can?(current_user, :read_build, @candidate.ci_build)
- end
+ def show; end
def destroy
@experiment = @candidate.experiment
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index cd2db2dad2c..516aa70cf89 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -166,6 +166,8 @@ class Projects::PipelinesController < Projects::ApplicationController
@stage = pipeline.stage(params[:stage])
return not_found unless @stage
+ return unless stage_stale?
+
render json: StageSerializer
.new(project: @project, current_user: @current_user)
.represent(@stage, details: true, retried: params[:retried])
@@ -263,6 +265,14 @@ class Projects::PipelinesController < Projects::ApplicationController
redirect_to url_for(safe_params.except(:scope).merge(status: safe_params[:scope])), status: :moved_permanently
end
+ def stage_stale?
+ return true if Feature.disabled?(:pipeline_stage_set_last_modified, @current_user)
+
+ last_modified = [@stage.updated_at.utc, @stage.statuses.maximum(:updated_at)].max
+
+ stale?(last_modified: last_modified, etag: @stage)
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def pipeline
return @pipeline if defined?(@pipeline)
diff --git a/app/controllers/projects/service_desk/custom_email_controller.rb b/app/controllers/projects/service_desk/custom_email_controller.rb
index fb5e87f9a97..7c1623cfcd1 100644
--- a/app/controllers/projects/service_desk/custom_email_controller.rb
+++ b/app/controllers/projects/service_desk/custom_email_controller.rb
@@ -3,7 +3,6 @@
module Projects
module ServiceDesk
class CustomEmailController < Projects::ApplicationController
- before_action :check_feature_flag_enabled
before_action :authorize_admin_project!
feature_category :service_desk
@@ -75,10 +74,6 @@ module Projects
error_message: error_message
}
end
-
- def check_feature_flag_enabled
- render_404 unless Feature.enabled?(:service_desk_custom_email, @project)
- end
end
end
end
diff --git a/app/controllers/projects/service_desk_controller.rb b/app/controllers/projects/service_desk_controller.rb
index 70cb439c4f3..a53e8859ee6 100644
--- a/app/controllers/projects/service_desk_controller.rb
+++ b/app/controllers/projects/service_desk_controller.rb
@@ -29,7 +29,13 @@ class Projects::ServiceDeskController < Projects::ApplicationController
end
def allowed_update_attributes
- %i[issue_template_key outgoing_name project_key add_external_participants_from_cc]
+ %i[
+ issue_template_key
+ outgoing_name
+ project_key
+ reopen_issue_on_external_participant_note
+ add_external_participants_from_cc
+ ]
end
def service_desk_attributes
@@ -42,6 +48,7 @@ class Projects::ServiceDeskController < Projects::ApplicationController
template_file_missing: service_desk_settings&.issue_template_missing?,
outgoing_name: service_desk_settings&.outgoing_name,
project_key: service_desk_settings&.project_key,
+ reopen_issue_on_external_participant_note: service_desk_settings&.reopen_issue_on_external_participant_note,
add_external_participants_from_cc: service_desk_settings&.add_external_participants_from_cc
}
end
diff --git a/app/controllers/projects/settings/merge_requests_controller.rb b/app/controllers/projects/settings/merge_requests_controller.rb
index f09e324f574..2724e2d9eec 100644
--- a/app/controllers/projects/settings/merge_requests_controller.rb
+++ b/app/controllers/projects/settings/merge_requests_controller.rb
@@ -52,6 +52,7 @@ module Projects
:resolve_outdated_diff_discussions,
:only_allow_merge_if_all_discussions_are_resolved,
:only_allow_merge_if_pipeline_succeeds,
+ :allow_merge_without_pipeline,
:printing_merge_request_link_enabled,
:remove_source_branch_after_merge,
:merge_method,
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index cfcc27edf3e..1bbf272e8f9 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -22,6 +22,7 @@ class Projects::TreeController < Projects::ApplicationController
push_frontend_feature_flag(:blob_blame_info, @project)
push_frontend_feature_flag(:highlight_js_worker, @project)
push_frontend_feature_flag(:explain_code_chat, current_user)
+ push_frontend_feature_flag(:encoding_logs_tree)
push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks)
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index cee56dca538..1152bdcf058 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -42,8 +42,8 @@ class ProjectsController < Projects::ApplicationController
push_frontend_feature_flag(:highlight_js_worker, @project)
push_frontend_feature_flag(:remove_monitor_metrics, @project)
push_frontend_feature_flag(:explain_code_chat, current_user)
- push_frontend_feature_flag(:service_desk_custom_email, @project)
push_frontend_feature_flag(:issue_email_participants, @project)
+ push_frontend_feature_flag(:encoding_logs_tree)
# TODO: We need to remove the FF eventually when we rollout page_specific_styles
push_frontend_feature_flag(:page_specific_styles, current_user)
push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks)
@@ -398,6 +398,7 @@ class ProjectsController < Projects::ApplicationController
if can?(current_user, :read_code, @project)
return render 'projects/no_repo' unless @project.repository_exists?
+ return render 'projects/missing_default_branch', status: :service_unavailable if @ref == ''
render 'projects/empty' if @project.empty_repo?
else
@@ -442,6 +443,7 @@ class ProjectsController < Projects::ApplicationController
params.require(:project)
.permit(project_params_attributes + attributes)
.merge(import_url_params)
+ .merge(object_format_params)
end
def project_feature_attributes
@@ -465,6 +467,7 @@ class ProjectsController < Projects::ApplicationController
monitor_access_level
infrastructure_access_level
model_experiments_access_level
+ model_registry_access_level
]
end
@@ -497,6 +500,7 @@ class ProjectsController < Projects::ApplicationController
:name,
:only_allow_merge_if_all_discussions_are_resolved,
:only_allow_merge_if_pipeline_succeeds,
+ :allow_merge_without_pipeline,
:path,
:printing_merge_request_link_enabled,
:public_builds,
@@ -529,6 +533,12 @@ class ProjectsController < Projects::ApplicationController
{}
end
+ def object_format_params
+ return {} unless Gitlab::Utils.to_boolean(params.dig(:project, :use_sha256_repository))
+
+ { repository_object_format: Repository::FORMAT_SHA256 }
+ end
+
def active_new_project_tab
project_params[:import_url].present? ? 'import' : 'blank'
end
@@ -552,6 +562,9 @@ class ProjectsController < Projects::ApplicationController
# Override get_id from ExtractsPath in this case is just the root of the default branch.
def get_id
project.repository.root_ref
+ rescue Gitlab::Git::CommandError
+ # Empty string is intentional and prevent the @ref reload
+ ''
end
def build_canonical_path(project)
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index 72636a89433..ed0f1687420 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -23,6 +23,7 @@ class RegistrationsController < Devise::RegistrationsController
before_action :load_recaptcha, only: :new
before_action only: [:create] do
check_rate_limit!(:user_sign_up, scope: request.ip)
+ invite_email # set for failure path so we still remember we are invite in form
end
feature_category :instance_resiliency
@@ -271,10 +272,15 @@ class RegistrationsController < Devise::RegistrationsController
def set_invite_params
if resource.email.blank? && params[:invite_email].present?
- resource.email = @invite_email = ActionController::Base.helpers.sanitize(params[:invite_email])
+ resource.email = invite_email
end
end
+ def invite_email
+ ActionController::Base.helpers.sanitize(params[:invite_email])
+ end
+ strong_memoize_attr :invite_email
+
def user_invited?
!!member_id
end
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index b639a9dda3f..64d9db41a1b 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -27,7 +27,10 @@ class SearchController < ApplicationController
around_action :allow_gitaly_ref_name_caching
- before_action :block_anonymous_global_searches, :check_scope_global_search_enabled, except: :opensearch
+ before_action :block_all_anonymous_searches,
+ :block_anonymous_global_searches,
+ :check_scope_global_search_enabled,
+ except: :opensearch
skip_before_action :authenticate_user!
requires_cross_project_access if: -> do
@@ -191,17 +194,7 @@ class SearchController < ApplicationController
# Merging to :metadata will ensure these are logged as top level keys
payload[:metadata] ||= {}
- payload[:metadata]['meta.search.group_id'] = params[:group_id]
- payload[:metadata]['meta.search.project_id'] = params[:project_id]
- payload[:metadata]['meta.search.scope'] = params[:scope] || @scope
- payload[:metadata]['meta.search.filters.confidential'] = params[:confidential]
- payload[:metadata]['meta.search.filters.state'] = params[:state]
- payload[:metadata]['meta.search.force_search_results'] = params[:force_search_results]
- payload[:metadata]['meta.search.project_ids'] = params[:project_ids]
- payload[:metadata]['meta.search.filters.language'] = params[:language]
- payload[:metadata]['meta.search.type'] = @search_type if @search_type.present?
- payload[:metadata]['meta.search.level'] = @search_level if @search_level.present?
- payload[:metadata][:global_search_duration_s] = @global_search_duration_s if @global_search_duration_s.present?
+ payload[:metadata].merge!(payload_metadata)
if search_service.abuse_detected?
payload[:metadata]['abuse.confidence'] = Gitlab::Abuse.confidence(:certain)
@@ -209,6 +202,23 @@ class SearchController < ApplicationController
end
end
+ def payload_metadata
+ {}.tap do |metadata|
+ metadata['meta.search.group_id'] = params[:group_id]
+ metadata['meta.search.project_id'] = params[:project_id]
+ metadata['meta.search.scope'] = params[:scope] || @scope
+ metadata['meta.search.page'] = params[:page] || '1'
+ metadata['meta.search.filters.confidential'] = params[:confidential]
+ metadata['meta.search.filters.state'] = params[:state]
+ metadata['meta.search.force_search_results'] = params[:force_search_results]
+ metadata['meta.search.project_ids'] = params[:project_ids]
+ metadata['meta.search.filters.language'] = params[:language]
+ metadata['meta.search.type'] = @search_type if @search_type.present?
+ metadata['meta.search.level'] = @search_level if @search_level.present?
+ metadata[:global_search_duration_s] = @global_search_duration_s if @global_search_duration_s.present?
+ end
+ end
+
def block_anonymous_global_searches
return unless search_service.global_search?
return if current_user
@@ -219,6 +229,14 @@ class SearchController < ApplicationController
redirect_to new_user_session_path, alert: _('You must be logged in to search across all of GitLab')
end
+ def block_all_anonymous_searches
+ return if current_user || ::Feature.enabled?(:allow_anonymous_searches, type: :ops)
+
+ store_location_for(:user, request.fullpath)
+
+ redirect_to new_user_session_path, alert: _('You must be logged in to search')
+ end
+
def check_scope_global_search_enabled
return unless search_service.global_search?
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 595d79abcf2..a8a09bd6ac6 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -100,7 +100,7 @@ class SessionsController < Devise::SessionsController
def after_pending_invitations_hook
member = resource.members.last
- store_location_for(:user, member.source.activity_path) if member
+ store_location_for(:user, polymorphic_path(member.source)) if member
end
def captcha_enabled?
diff --git a/app/controllers/user_settings/active_sessions_controller.rb b/app/controllers/user_settings/active_sessions_controller.rb
new file mode 100644
index 00000000000..bfc969d0ff8
--- /dev/null
+++ b/app/controllers/user_settings/active_sessions_controller.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module UserSettings
+ class ActiveSessionsController < ApplicationController
+ feature_category :system_access
+
+ def index
+ @sessions = ActiveSession.list(current_user).reject(&:is_impersonated)
+ end
+
+ def destroy
+ # params[:id] can be an Rack::Session::SessionId#private_id
+ ActiveSession.destroy_session(current_user, params[:id])
+ current_user.forget_me!
+
+ respond_to do |format|
+ format.html { redirect_to user_settings_active_sessions_url, status: :found }
+ format.js { head :ok }
+ end
+ end
+ end
+end
diff --git a/app/controllers/user_settings/application_controller.rb b/app/controllers/user_settings/application_controller.rb
new file mode 100644
index 00000000000..d495c604bf3
--- /dev/null
+++ b/app/controllers/user_settings/application_controller.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module UserSettings
+ class ApplicationController < ::ApplicationController
+ layout 'profile'
+ end
+end
diff --git a/app/controllers/user_settings/passwords_controller.rb b/app/controllers/user_settings/passwords_controller.rb
new file mode 100644
index 00000000000..d68ddf90d49
--- /dev/null
+++ b/app/controllers/user_settings/passwords_controller.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+module UserSettings
+ class PasswordsController < ApplicationController
+ include Gitlab::Tracking::Helpers::WeakPasswordErrorEvent
+
+ skip_before_action :check_password_expiration, only: [:new, :create]
+ skip_before_action :check_two_factor_requirement, only: [:new, :create]
+
+ before_action :set_user
+ before_action :authorize_change_password!
+
+ layout :determine_layout
+
+ feature_category :system_access
+
+ def new; end
+
+ def create
+ unless @user.password_automatically_set || @user.valid_password?(user_params[:password])
+ redirect_to new_user_settings_password_path, alert: _('You must provide a valid current password')
+ return
+ end
+
+ result = Users::UpdateService.new(current_user, password_attributes.merge(user: @user)).execute
+
+ if result[:status] == :success
+ Users::UpdateService.new(current_user, user: @user, password_expires_at: nil).execute
+
+ redirect_to root_path, notice: _('Password successfully changed')
+ else
+ track_weak_password_error(@user, self.class.name, 'create')
+ render :new
+ end
+ end
+
+ def edit; end
+
+ def update
+ unless @user.password_automatically_set || @user.valid_password?(user_params[:password])
+ handle_invalid_current_password_attempt!
+
+ redirect_to edit_user_settings_password_path, alert: _('You must provide a valid current password')
+ return
+ end
+
+ result = Users::UpdateService.new(current_user, password_attributes.merge(user: @user)).execute
+
+ if result[:status] == :success
+ flash[:notice] = _('Password was successfully updated. Please sign in again.')
+ redirect_to new_user_session_path
+ else
+ track_weak_password_error(@user, self.class.name, 'update')
+ @user.reset
+ render 'edit'
+ end
+ end
+
+ def reset
+ current_user.send_reset_password_instructions
+ redirect_to edit_user_settings_password_path, notice: _('We sent you an email with reset password instructions')
+ end
+
+ private
+
+ def set_user
+ @user = current_user
+ end
+
+ def determine_layout
+ if [:new, :create].include?(action_name.to_sym)
+ 'application'
+ else
+ 'profile'
+ end
+ end
+
+ def authorize_change_password!
+ render_404 unless @user.allow_password_authentication?
+ end
+
+ def handle_invalid_current_password_attempt!
+ Gitlab::AppLogger.info(message: 'Invalid current password when attempting to update user password',
+ username: @user.username, ip: request.remote_ip)
+
+ @user.increment_failed_attempts!
+ end
+
+ def user_params
+ params.require(:user).permit(:password, :new_password, :password_confirmation)
+ end
+
+ def password_attributes
+ {
+ password: user_params[:new_password],
+ password_confirmation: user_params[:password_confirmation],
+ password_automatically_set: false
+ }
+ end
+ end
+end
+
+UserSettings::PasswordsController.prepend_mod
diff --git a/app/controllers/user_settings/personal_access_tokens_controller.rb b/app/controllers/user_settings/personal_access_tokens_controller.rb
new file mode 100644
index 00000000000..a8e9a328c26
--- /dev/null
+++ b/app/controllers/user_settings/personal_access_tokens_controller.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+module UserSettings
+ class PersonalAccessTokensController < ApplicationController
+ include RenderAccessTokens
+
+ feature_category :system_access
+
+ 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?
+ @personal_access_token = finder.build(
+ name: params[:name],
+ scopes: scopes
+ )
+
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: @active_access_tokens
+ end
+ end
+ end
+
+ def create
+ result = ::PersonalAccessTokens::CreateService.new(
+ current_user: current_user,
+ target_user: current_user,
+ params: personal_access_token_params,
+ concatenate_errors: false
+ ).execute
+
+ @personal_access_token = result.payload[:personal_access_token]
+
+ if result.success?
+ render json: { new_token: @personal_access_token.token,
+ active_access_tokens: active_access_tokens }, status: :ok
+ else
+ render json: { errors: result.errors }, status: :unprocessable_entity
+ end
+ end
+
+ def revoke
+ @personal_access_token = finder.find(params[:id])
+ service = PersonalAccessTokens::RevokeService.new(current_user, token: @personal_access_token).execute
+ service.success? ? flash[:notice] = service.message : flash[:alert] = service.message
+
+ redirect_to user_settings_personal_access_tokens_path
+ end
+
+ private
+
+ def finder(options = {})
+ PersonalAccessTokensFinder.new({ user: current_user, impersonation: false }.merge(options))
+ end
+
+ def personal_access_token_params
+ params.require(:personal_access_token).permit(:name, :expires_at, scopes: [])
+ end
+
+ def set_index_vars
+ @scopes = Gitlab::Auth.available_scopes_for(current_user)
+ @active_access_tokens = active_access_tokens
+ end
+
+ def represent(tokens)
+ ::PersonalAccessTokenSerializer.new.represent(tokens)
+ end
+
+ def check_personal_access_tokens_enabled
+ render_404 if Gitlab::CurrentSettings.personal_access_tokens_disabled?
+ end
+ end
+end
diff --git a/app/controllers/user_settings/user_settings_controller.rb b/app/controllers/user_settings/user_settings_controller.rb
new file mode 100644
index 00000000000..3535a30095a
--- /dev/null
+++ b/app/controllers/user_settings/user_settings_controller.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module UserSettings
+ class UserSettingsController < ApplicationController
+ feature_category :system_access
+
+ def authentication_log
+ @events = AuthenticationEvent.for_user(current_user)
+ .order_by_created_at_desc
+ .page(params[:page])
+
+ Gitlab::Tracking.event(self.class.name, 'search_audit_event', user: current_user)
+ end
+ end
+end
diff --git a/app/controllers/web_ide/remote_ide_controller.rb b/app/controllers/web_ide/remote_ide_controller.rb
index 90652a1b6e2..8392e7a190c 100644
--- a/app/controllers/web_ide/remote_ide_controller.rb
+++ b/app/controllers/web_ide/remote_ide_controller.rb
@@ -17,7 +17,7 @@ module WebIde
def index
return render_404 unless Feature.enabled?(:vscode_web_ide, current_user)
- render layout: 'fullscreen', locals: { minimal: true, data: root_element_data }
+ render layout: 'fullscreen', locals: { data: root_element_data }
end
private
diff --git a/app/controllers/well_known_controller.rb b/app/controllers/well_known_controller.rb
new file mode 100644
index 00000000000..9ca38a81b11
--- /dev/null
+++ b/app/controllers/well_known_controller.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+# This controller implements /.well-known paths that have no better home.
+#
+# Other controllers also implement /.well-known/* paths. They can be
+# discovered by running `rails routes | grep "well-known"`.
+class WellKnownController < ApplicationController # rubocop:disable Gitlab/NamespacedClass -- No relevant product domain exists
+ skip_before_action :authenticate_user!, :check_two_factor_requirement
+ feature_category :compliance_management, [:security_txt]
+
+ def security_txt
+ content = Gitlab::CurrentSettings.current_application_settings.security_txt_content
+ if content.present?
+ render plain: content
+ else
+ route_not_found
+ end
+ end
+end