Commit 4ede4460 authored by Stan Hu's avatar Stan Hu

Fix SSO SAML redirection not including query string

When a user without a SSO session attempted to access anything in a SAML
group with a query string, previously GitLab would redirect the user
back to original path but drop the query string.

We fixed the redirection originally in
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66791, but
`request.path_info` drops query strings. To ensure the query strings are
passed to the `RelayState`, we need to use `request.fullpath`.

Relates to https://gitlab.com/gitlab-org/gitlab/-/issues/338833

Changelog: fixed
parent 1dae49eb
...@@ -307,7 +307,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -307,7 +307,7 @@ class Admin::UsersController < Admin::ApplicationController
end end
def user def user
@user ||= find_routable!(User, params[:id], request.path_info) @user ||= find_routable!(User, params[:id], request.fullpath)
end end
def build_canonical_path(user) def build_canonical_path(user)
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
module ProjectUnauthorized module ProjectUnauthorized
module ControllerActions module ControllerActions
def self.on_routable_not_found def self.on_routable_not_found
lambda do |routable, path_info| lambda do |routable, full_path|
return unless routable.is_a?(Project) return unless routable.is_a?(Project)
label = routable.external_authorization_classification_label label = routable.external_authorization_classification_label
......
...@@ -3,13 +3,13 @@ ...@@ -3,13 +3,13 @@
module RoutableActions module RoutableActions
extend ActiveSupport::Concern extend ActiveSupport::Concern
def find_routable!(routable_klass, routable_full_path, path_info, extra_authorization_proc: nil) def find_routable!(routable_klass, routable_full_path, full_path, extra_authorization_proc: nil)
routable = routable_klass.find_by_full_path(routable_full_path, follow_redirects: request.get?) routable = routable_klass.find_by_full_path(routable_full_path, follow_redirects: request.get?)
if routable_authorized?(routable, extra_authorization_proc) if routable_authorized?(routable, extra_authorization_proc)
ensure_canonical_path(routable, routable_full_path) ensure_canonical_path(routable, routable_full_path)
routable routable
else else
perform_not_found_actions(routable, not_found_actions, path_info) perform_not_found_actions(routable, not_found_actions, full_path)
route_not_found unless performed? route_not_found unless performed?
...@@ -21,11 +21,11 @@ module RoutableActions ...@@ -21,11 +21,11 @@ module RoutableActions
[ProjectUnauthorized::ControllerActions.on_routable_not_found] [ProjectUnauthorized::ControllerActions.on_routable_not_found]
end end
def perform_not_found_actions(routable, actions, path_info) def perform_not_found_actions(routable, actions, full_path)
actions.each do |action| actions.each do |action|
break if performed? break if performed?
instance_exec(routable, path_info, &action) instance_exec(routable, full_path, &action)
end end
end end
......
...@@ -24,7 +24,7 @@ class Groups::ApplicationController < ApplicationController ...@@ -24,7 +24,7 @@ class Groups::ApplicationController < ApplicationController
end end
def group def group
@group ||= find_routable!(Group, params[:group_id] || params[:id], request.path_info) @group ||= find_routable!(Group, params[:group_id] || params[:id], request.fullpath)
end end
def group_projects def group_projects
......
...@@ -13,6 +13,6 @@ class Groups::Clusters::IntegrationsController < Clusters::IntegrationsControlle ...@@ -13,6 +13,6 @@ class Groups::Clusters::IntegrationsController < Clusters::IntegrationsControlle
end end
def group def group
@group ||= find_routable!(Group, params[:group_id] || params[:id], request.path_info) @group ||= find_routable!(Group, params[:group_id] || params[:id], request.fullpath)
end end
end end
...@@ -15,7 +15,7 @@ class Groups::ClustersController < Clusters::ClustersController ...@@ -15,7 +15,7 @@ class Groups::ClustersController < Clusters::ClustersController
end end
def group def group
@group ||= find_routable!(Group, params[:group_id] || params[:id], request.path_info) @group ||= find_routable!(Group, params[:group_id] || params[:id], request.fullpath)
end end
def metrics_dashboard_params def metrics_dashboard_params
......
...@@ -6,7 +6,7 @@ class Profiles::GroupsController < Profiles::ApplicationController ...@@ -6,7 +6,7 @@ class Profiles::GroupsController < Profiles::ApplicationController
feature_category :users feature_category :users
def update def update
group = find_routable!(Group, params[:id], request.path_info) group = find_routable!(Group, params[:id], request.fullpath)
notification_setting = current_user.notification_settings_for(group) notification_setting = current_user.notification_settings_for(group)
if notification_setting.update(update_params) if notification_setting.update(update_params)
......
...@@ -26,7 +26,7 @@ class Projects::ApplicationController < ApplicationController ...@@ -26,7 +26,7 @@ class Projects::ApplicationController < ApplicationController
path = File.join(params[:namespace_id], params[:project_id] || params[:id]) path = File.join(params[:namespace_id], params[:project_id] || params[:id])
auth_proc = ->(project) { !project.pending_delete? } auth_proc = ->(project) { !project.pending_delete? }
@project = find_routable!(Project, path, request.path_info, extra_authorization_proc: auth_proc) @project = find_routable!(Project, path, request.fullpath, extra_authorization_proc: auth_proc)
end end
def build_canonical_path(project) def build_canonical_path(project)
......
...@@ -10,6 +10,6 @@ class Projects::Clusters::IntegrationsController < ::Clusters::IntegrationsContr ...@@ -10,6 +10,6 @@ class Projects::Clusters::IntegrationsController < ::Clusters::IntegrationsContr
end end
def project def project
@project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), request.path_info) @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), request.fullpath)
end end
end end
...@@ -17,7 +17,7 @@ class Projects::ClustersController < Clusters::ClustersController ...@@ -17,7 +17,7 @@ class Projects::ClustersController < Clusters::ClustersController
end end
def project def project
@project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), request.path_info) @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), request.fullpath)
end end
def repository def repository
......
...@@ -172,7 +172,7 @@ class UsersController < ApplicationController ...@@ -172,7 +172,7 @@ class UsersController < ApplicationController
private private
def user def user
@user ||= find_routable!(User, params[:username], request.path_info) @user ||= find_routable!(User, params[:username], request.fullpath)
end end
def personal_projects def personal_projects
......
...@@ -6,11 +6,11 @@ module EE ...@@ -6,11 +6,11 @@ module EE
include ::Gitlab::Routing include ::Gitlab::Routing
include ::Gitlab::Utils::StrongMemoize include ::Gitlab::Utils::StrongMemoize
attr_reader :routable, :path_info attr_reader :routable, :full_path
def initialize(routable, path_info) def initialize(routable, full_path)
@routable = routable @routable = routable
@path_info = path_info @full_path = full_path
end end
def should_redirect_to_group_saml_sso?(current_user, request) def should_redirect_to_group_saml_sso?(current_user, request)
...@@ -26,8 +26,8 @@ module EE ...@@ -26,8 +26,8 @@ module EE
module ControllerActions module ControllerActions
def self.on_routable_not_found def self.on_routable_not_found
lambda do |routable, path_info| lambda do |routable, full_path|
redirector = SsoEnforcementRedirect.new(routable, path_info) redirector = SsoEnforcementRedirect.new(routable, full_path)
if redirector.should_redirect_to_group_saml_sso?(current_user, request) if redirector.should_redirect_to_group_saml_sso?(current_user, request)
redirect_to redirector.sso_redirect_url redirect_to redirector.sso_redirect_url
...@@ -64,7 +64,7 @@ module EE ...@@ -64,7 +64,7 @@ module EE
def url_params def url_params
{ {
token: root_group.saml_discovery_token, token: root_group.saml_discovery_token,
redirect: path_info redirect: full_path
} }
end end
end end
......
...@@ -23,13 +23,13 @@ class Groups::Analytics::ApplicationController < ApplicationController ...@@ -23,13 +23,13 @@ class Groups::Analytics::ApplicationController < ApplicationController
def load_group def load_group
return unless params['group_id'] return unless params['group_id']
@group = find_routable!(Group, params['group_id'], request.path_info) @group = find_routable!(Group, params['group_id'], request.fullpath)
end end
def load_project def load_project
return unless @group && params['project_id'] return unless @group && params['project_id']
@project = find_routable!(@group.projects, params['project_id'], request.path_info) @project = find_routable!(@group.projects, params['project_id'], request.fullpath)
end end
private_class_method :increment_usage_counter private_class_method :increment_usage_counter
......
...@@ -31,7 +31,7 @@ module Subscriptions ...@@ -31,7 +31,7 @@ module Subscriptions
private private
def find_group def find_group
@group ||= find_routable!(Group, params[:id], request.path_info) @group ||= find_routable!(Group, params[:id], request.fullpath)
end end
def group_params def group_params
......
...@@ -122,7 +122,7 @@ RSpec.describe 'SAML access enforcement' do ...@@ -122,7 +122,7 @@ RSpec.describe 'SAML access enforcement' do
context 'with a merge request' do context 'with a merge request' do
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) } let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:resource_path) { project_merge_request_path(project, merge_request) } let(:resource_path) { project_merge_request_path(project, merge_request, { test: "value" }) }
it 'redirects to the SSO page and then merge request page after login' do it 'redirects to the SSO page and then merge request page after login' do
visit resource_path visit resource_path
...@@ -131,7 +131,8 @@ RSpec.describe 'SAML access enforcement' do ...@@ -131,7 +131,8 @@ RSpec.describe 'SAML access enforcement' do
click_link 'Sign in with Single Sign-On' click_link 'Sign in with Single Sign-On'
expect(current_path).to eq(resource_path) # Capybara's have_current_path matcher checks the path and query string
expect(page).to have_current_path(resource_path)
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment