Commit 2731aa70 authored by Mikołaj Wawrzyniak's avatar Mikołaj Wawrzyniak

Merge branch 'handle-new-action-mask-urls' into 'master'

Handle new action and other missed actions

See merge request gitlab-org/gitlab!71183
parents eeda9e91 9b422fd6
...@@ -2,58 +2,86 @@ ...@@ -2,58 +2,86 @@
module Routing module Routing
module PseudonymizationHelper module PseudonymizationHelper
def masked_page_url class MaskHelper
return unless Feature.enabled?(:mask_page_urls, type: :ops) QUERY_PARAMS_TO_MASK = %w[
assignee_username
author_username
].freeze
mask_params(Rails.application.routes.recognize_path(request.original_fullpath)) def initialize(request_object, group, project)
rescue ActionController::RoutingError, URI::InvalidURIError => e @request = request_object
Gitlab::ErrorTracking.track_exception(e, url: request.original_fullpath) @group = group
nil @project = project
end end
private def mask_params
return default_root_url + @request.original_fullpath unless has_maskable_params?
def mask_params(request_params) masked_params = @request.path_parameters.to_h do |key, value|
return if request_params[:action] == 'new' case key
when :project_id
[key, "project#{@project&.id}"]
when :namespace_id
namespace = @group || @project&.namespace
[key, "namespace#{namespace&.id}"]
when :id
[key, mask_id(value)]
else
[key, value]
end
end
namespace_type = request_params[:controller].split('/')[1] Gitlab::Routing.url_helpers.url_for(masked_params.merge(masked_query_params))
end
namespace_type.present? ? url_with_namespace_type(request_params, namespace_type) : url_without_namespace_type(request_params) private
end
def url_without_namespace_type(request_params) def mask_id(value)
masked_url = "#{request.protocol}#{request.host_with_port}" if @request.path_parameters[:controller] == 'projects/blob'
':repository_path'
elsif @request.path_parameters[:controller] == 'projects'
"project#{@project&.id}"
elsif @request.path_parameters[:controller] == 'groups'
"namespace#{@group&.id}"
else
value
end
end
masked_url += case request_params[:controller] def has_maskable_params?
when 'groups' request_params = @request.path_parameters.to_h
"/namespace:#{group.id}" request_params.has_key?(:namespace_id) || request_params.has_key?(:project_id) || request_params.has_key?(:id) || @request.query_string.present?
when 'projects' end
"/namespace:#{project.namespace_id}/project:#{project.id}"
when 'root'
''
else
"#{request.path}"
end
masked_url += request.query_string.present? ? "?#{request.query_string}" : '' def masked_query_params
return {} unless @request.query_string.present?
masked_url query_string_hash = Rack::Utils.parse_nested_query(@request.query_string)
end
QUERY_PARAMS_TO_MASK.each do |maskable_attribute|
next unless query_string_hash.has_key?(maskable_attribute)
def url_with_namespace_type(request_params, namespace_type) query_string_hash[maskable_attribute] = "masked_#{maskable_attribute}"
masked_url = "#{request.protocol}#{request.host_with_port}" end
if request_params.has_key?(:project_id) query_string_hash
masked_url += "/namespace:#{project.namespace_id}/project:#{project.id}/-/#{namespace_type}"
end end
if request_params.has_key?(:id) def default_root_url
masked_url += namespace_type == 'blob' ? '/:repository_path' : "/#{request_params[:id]}" Gitlab::Routing.url_helpers.root_url(only_path: false)
end end
end
masked_url += request.query_string.present? ? "?#{request.query_string}" : '' def masked_page_url
return unless Feature.enabled?(:mask_page_urls, type: :ops)
masked_url current_group = group if defined?(group)
current_project = project if defined?(project)
mask_helper = MaskHelper.new(request, current_group, current_project)
mask_helper.mask_params
rescue ActionController::RoutingError, URI::InvalidURIError => e
Gitlab::ErrorTracking.track_exception(e, url: request.original_fullpath)
nil
end end
end end
end end
...@@ -25,95 +25,155 @@ RSpec.describe ::Routing::PseudonymizationHelper do ...@@ -25,95 +25,155 @@ RSpec.describe ::Routing::PseudonymizationHelper do
describe 'when url has params to mask' do describe 'when url has params to mask' do
context 'with controller for MR' do context 'with controller for MR' do
let(:masked_url) { "http://test.host/namespace:#{group.id}/project:#{project.id}/-/merge_requests/#{merge_request.id}" } let(:masked_url) { "http://localhost/namespace#{group.id}/project#{project.id}/-/merge_requests/#{merge_request.id}" }
let(:request) do
double(:Request,
path_parameters: {
controller: "projects/merge_requests",
action: "show",
namespace_id: group.name,
project_id: project.name,
id: merge_request.id.to_s
},
protocol: 'http',
host: 'localhost',
query_string: '')
end
before do before do
allow(Rails.application.routes).to receive(:recognize_path).and_return({ allow(helper).to receive(:request).and_return(request)
controller: "projects/merge_requests",
action: "show",
namespace_id: group.name,
project_id: project.name,
id: merge_request.id.to_s
})
end end
it_behaves_like 'masked url' it_behaves_like 'masked url'
end end
context 'with controller for issue' do context 'with controller for issue' do
let(:masked_url) { "http://test.host/namespace:#{group.id}/project:#{project.id}/-/issues/#{issue.id}" } let(:masked_url) { "http://localhost/namespace#{group.id}/project#{project.id}/-/issues/#{issue.id}" }
let(:request) do
double(:Request,
path_parameters: {
controller: "projects/issues",
action: "show",
namespace_id: group.name,
project_id: project.name,
id: issue.id.to_s
},
protocol: 'http',
host: 'localhost',
query_string: '')
end
before do before do
allow(Rails.application.routes).to receive(:recognize_path).and_return({ allow(helper).to receive(:request).and_return(request)
controller: "projects/issues",
action: "show",
namespace_id: group.name,
project_id: project.name,
id: issue.id.to_s
})
end end
it_behaves_like 'masked url' it_behaves_like 'masked url'
end end
context 'with controller for groups with subgroups and project' do context 'with controller for groups with subgroups and project' do
let(:masked_url) { "http://test.host/namespace:#{subgroup.id}/project:#{subproject.id}"} let(:masked_url) { "http://localhost/namespace#{subgroup.id}/project#{subproject.id}"}
let(:request) do
double(:Request,
path_parameters: {
controller: 'projects',
action: 'show',
namespace_id: subgroup.name,
id: subproject.name
},
protocol: 'http',
host: 'localhost',
query_string: '')
end
before do before do
allow(helper).to receive(:group).and_return(subgroup) allow(helper).to receive(:group).and_return(subgroup)
allow(helper).to receive(:project).and_return(subproject) allow(helper).to receive(:project).and_return(subproject)
allow(Rails.application.routes).to receive(:recognize_path).and_return({ allow(helper).to receive(:request).and_return(request)
controller: 'projects',
action: 'show',
namespace_id: subgroup.name,
id: subproject.name
})
end end
it_behaves_like 'masked url' it_behaves_like 'masked url'
end end
context 'with controller for groups and subgroups' do context 'with controller for groups and subgroups' do
let(:masked_url) { "http://test.host/namespace:#{subgroup.id}"} let(:masked_url) { "http://localhost/groups/namespace#{subgroup.id}/-/shared"}
let(:request) do
double(:Request,
path_parameters: {
controller: 'groups',
action: 'show',
id: subgroup.name
},
protocol: 'http',
host: 'localhost',
query_string: '')
end
before do before do
allow(helper).to receive(:group).and_return(subgroup) allow(helper).to receive(:group).and_return(subgroup)
allow(Rails.application.routes).to receive(:recognize_path).and_return({ allow(helper).to receive(:request).and_return(request)
controller: 'groups',
action: 'show',
id: subgroup.name
})
end end
it_behaves_like 'masked url' it_behaves_like 'masked url'
end end
context 'with controller for blob with file path' do context 'with controller for blob with file path' do
let(:masked_url) { "http://test.host/namespace:#{group.id}/project:#{project.id}/-/blob/:repository_path" } let(:masked_url) { "http://localhost/namespace#{group.id}/project#{project.id}/-/blob/:repository_path" }
let(:request) do
double(:Request,
path_parameters: {
controller: 'projects/blob',
action: 'show',
namespace_id: group.name,
project_id: project.name,
id: 'master/README.md'
},
protocol: 'http',
host: 'localhost',
query_string: '')
end
before do before do
allow(Rails.application.routes).to receive(:recognize_path).and_return({ allow(helper).to receive(:request).and_return(request)
controller: 'projects/blob',
action: 'show',
namespace_id: group.name,
project_id: project.name,
id: 'master/README.md'
})
end end
it_behaves_like 'masked url' it_behaves_like 'masked url'
end end
context 'with non identifiable controller' do context 'when assignee_username is present' do
let(:masked_url) { "http://test.host/dashboard/issues?assignee_username=root" } let(:masked_url) { "http://localhost/dashboard/issues?assignee_username=masked_assignee_username" }
let(:request) do
double(:Request,
path_parameters: {
controller: 'dashboard',
action: 'issues'
},
protocol: 'http',
host: 'localhost',
query_string: 'assignee_username=root')
end
before do before do
controller.request.path = '/dashboard/issues' allow(helper).to receive(:request).and_return(request)
controller.request.query_string = 'assignee_username=root' end
allow(Rails.application.routes).to receive(:recognize_path).and_return({
controller: 'dashboard', it_behaves_like 'masked url'
action: 'issues' end
})
context 'when author_username is present' do
let(:masked_url) { "http://localhost/dashboard/issues?author_username=masked_author_username&scope=all&state=opened" }
let(:request) do
double(:Request,
path_parameters: {
controller: 'dashboard',
action: 'issues'
},
protocol: 'http',
host: 'localhost',
query_string: 'author_username=root&scope=all&state=opened')
end
before do
allow(helper).to receive(:request).and_return(request)
end end
it_behaves_like 'masked url' it_behaves_like 'masked url'
...@@ -121,9 +181,13 @@ RSpec.describe ::Routing::PseudonymizationHelper do ...@@ -121,9 +181,13 @@ RSpec.describe ::Routing::PseudonymizationHelper do
end end
describe 'when url has no params to mask' do describe 'when url has no params to mask' do
let(:root_url) { 'http://test.host' } let(:root_url) { 'http://localhost/some/path' }
context 'returns root url' do context 'returns root url' do
before do
controller.request.path = 'some/path'
end
it 'masked_page_url' do it 'masked_page_url' do
expect(helper.masked_page_url).to eq(root_url) expect(helper.masked_page_url).to eq(root_url)
end end
...@@ -132,17 +196,26 @@ RSpec.describe ::Routing::PseudonymizationHelper do ...@@ -132,17 +196,26 @@ RSpec.describe ::Routing::PseudonymizationHelper do
describe 'when it raises exception' do describe 'when it raises exception' do
context 'calls error tracking' do context 'calls error tracking' do
let(:request) do
double(:Request,
path_parameters: {
controller: 'dashboard',
action: 'issues'
},
protocol: 'http',
host: 'localhost',
query_string: 'assignee_username=root',
original_fullpath: '/dashboard/issues?assignee_username=root')
end
before do before do
controller.request.path = '/dashboard/issues' allow(helper).to receive(:request).and_return(request)
controller.request.query_string = 'assignee_username=root'
allow(Rails.application.routes).to receive(:recognize_path).and_return({
controller: 'dashboard',
action: 'issues'
})
end end
it 'sends error to sentry and returns nil' do it 'sends error to sentry and returns nil' do
allow(helper).to receive(:mask_params).with(anything).and_raise(ActionController::RoutingError, 'Some routing error') allow_next_instance_of(Routing::PseudonymizationHelper::MaskHelper) do |mask_helper|
allow(mask_helper).to receive(:mask_params).and_raise(ActionController::RoutingError, 'Some routing error')
end
expect(Gitlab::ErrorTracking).to receive(:track_exception).with( expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
ActionController::RoutingError, ActionController::RoutingError,
......
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