diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index e312a45839952adcd559919180b041be8311e5ef..74e32e00bbf7d3cf1fb5f38f96784df79a5767e7 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -502,6 +502,14 @@ def remote_mirror_setting_enabled? false end + def http_clone_url_to_repo(project) + project.http_url_to_repo + end + + def ssh_clone_url_to_repo(project) + project.ssh_url_to_repo + end + private def localized_access_names diff --git a/app/views/projects/buttons/_clone.html.haml b/app/views/projects/buttons/_clone.html.haml index a8a911adb7d45e1e66175c8d14d153e4c661b11a..ab026d9c6ac2d51ce1b2da51ce5239286dcb5bb6 100644 --- a/app/views/projects/buttons/_clone.html.haml +++ b/app/views/projects/buttons/_clone.html.haml @@ -13,7 +13,7 @@ %label.label-bold = _('Clone with SSH') .input-group.btn-group - = text_field_tag :ssh_project_clone, project.ssh_url_to_repo, class: "js-select-on-focus form-control", readonly: true, aria: { label: _('Repository clone URL') }, data: { qa_selector: 'ssh_clone_url_content' } + = text_field_tag :ssh_project_clone, ssh_clone_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true, aria: { label: _('Repository clone URL') }, data: { qa_selector: 'ssh_clone_url_content' } .input-group-append = clipboard_button(target: '#ssh_project_clone', title: _("Copy URL"), class: "input-group-text gl-button btn btn-icon btn-default") = render_if_exists 'projects/buttons/geo' @@ -22,7 +22,7 @@ %label.label-bold = _('Clone with %{http_label}') % { http_label: gitlab_config.protocol.upcase } .input-group.btn-group - = text_field_tag :http_project_clone, project.http_url_to_repo, class: "js-select-on-focus form-control", readonly: true, aria: { label: _('Repository clone URL') }, data: { qa_selector: 'http_clone_url_content' } + = text_field_tag :http_project_clone, http_clone_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true, aria: { label: _('Repository clone URL') }, data: { qa_selector: 'http_clone_url_content' } .input-group-append = clipboard_button(target: '#http_project_clone', title: _("Copy URL"), class: "input-group-text gl-button btn btn-icon btn-default") = render_if_exists 'projects/buttons/geo' @@ -32,12 +32,12 @@ %label.label-bold{ class: 'gl-px-4!' } = _('Open in your IDE') - if ssh_enabled? - - escaped_ssh_url_to_repo = CGI.escape(project.ssh_url_to_repo) + - escaped_ssh_url_to_repo = CGI.escape(ssh_clone_url_to_repo(project)) %a.dropdown-item.open-with-link{ href: 'vscode://vscode.git/clone?url=' + escaped_ssh_url_to_repo } .gl-dropdown-item-text-wrapper = _('Visual Studio Code (SSH)') - if http_enabled? - - escaped_http_url_to_repo = CGI.escape(project.http_url_to_repo) + - escaped_http_url_to_repo = CGI.escape(http_clone_url_to_repo(project)) %a.dropdown-item.open-with-link{ href: 'vscode://vscode.git/clone?url=' + escaped_http_url_to_repo } .gl-dropdown-item-text-wrapper = _('Visual Studio Code (HTTPS)') diff --git a/app/views/shared/_mobile_clone_panel.html.haml b/app/views/shared/_mobile_clone_panel.html.haml index 8b7ef838d2bf5abcdeff7db68eefc413279516e9..aa3043b8fd600e403acdc7619dea23791b0ea1b2 100644 --- a/app/views/shared/_mobile_clone_panel.html.haml +++ b/app/views/shared/_mobile_clone_panel.html.haml @@ -9,8 +9,8 @@ %ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right.clone-options-dropdown{ data: { dropdown: true } } - if ssh_enabled? %li - = dropdown_item_with_description(ssh_copy_label, project.ssh_url_to_repo, href: project.ssh_url_to_repo, data: { clone_type: 'ssh' }, default: true) + = dropdown_item_with_description(ssh_copy_label, ssh_clone_url_to_repo(project), href: ssh_clone_url_to_repo(project), data: { clone_type: 'ssh' }, default: true) - if http_enabled? %li - = dropdown_item_with_description(http_copy_label, project.http_url_to_repo, href: project.http_url_to_repo, data: { clone_type: 'http' }) + = dropdown_item_with_description(http_copy_label, http_clone_url_to_repo(project), href: http_clone_url_to_repo(project), data: { clone_type: 'http' }) = render_if_exists 'shared/mobile_kerberos_clone' diff --git a/ee/app/helpers/ee/gitlab_routing_helper.rb b/ee/app/helpers/ee/gitlab_routing_helper.rb index a27da02d29e207f4780a9eac2aaf0f930fd7fd79..d089929de04c984c7ded91302b96d4ecb5dddbfa 100644 --- a/ee/app/helpers/ee/gitlab_routing_helper.rb +++ b/ee/app/helpers/ee/gitlab_routing_helper.rb @@ -20,6 +20,20 @@ def geo_primary_http_url_to_repo(container) geo_primary_web_url(container) + '.git' end + def geo_proxied_http_url_to_repo(proxied_site, container) + "#{File.join(proxied_site.url, container.full_path)}.git" + end + + def geo_proxied_ssh_url_to_repo(proxied_site, container) + primary_ssh_url = container.ssh_url_to_repo + secondary_host = proxied_site.uri.host + + # Note: if ssh_port is configured to 22, resulting ssh url does not contain the port + # URI.parse does not parse urls with no port + # Substitute original ssh_host with the proxied site host + primary_ssh_url.gsub(::Gitlab.config.gitlab_shell.ssh_host, secondary_host) + end + def geo_primary_http_internal_url_to_repo(container) geo_primary_web_url(container, internal: true) + '.git' end diff --git a/ee/app/helpers/ee/projects_helper.rb b/ee/app/helpers/ee/projects_helper.rb index f55a5964ed9ebd67f20219ff6ecba59fdfb44130..7c5957fcf2435f875db3561b3e563ecb6b35681e 100644 --- a/ee/app/helpers/ee/projects_helper.rb +++ b/ee/app/helpers/ee/projects_helper.rb @@ -281,6 +281,20 @@ def project_compliance_framework_app_data(project, can_edit) end end + def proxied_site + ::Gitlab::Geo.proxied_site(request.env) + end + + override :http_clone_url_to_repo + def http_clone_url_to_repo(project) + ::Gitlab::Geo.proxied_request?(request.env) ? geo_proxied_http_url_to_repo(proxied_site, project) : super + end + + override :ssh_clone_url_to_repo + def ssh_clone_url_to_repo(project) + ::Gitlab::Geo.proxied_request?(request.env) ? geo_proxied_ssh_url_to_repo(proxied_site, project) : super + end + private def remove_message_data(project) diff --git a/ee/lib/gitlab/geo.rb b/ee/lib/gitlab/geo.rb index d7b1a95191bbc05aecf258c35f274553ef7e4717..42aa9614f186a71e3658f1ab56a9f4327cd85098 100644 --- a/ee/lib/gitlab/geo.rb +++ b/ee/lib/gitlab/geo.rb @@ -184,10 +184,14 @@ def self.proxied_site(env) return unless ::Gitlab::Geo.primary? return unless proxied_request?(env) && env[GEO_PROXIED_EXTRA_DATA_HEADER].present? - signed_data = Gitlab::Geo::SignedData.new - signed_data.decode_data(env[GEO_PROXIED_EXTRA_DATA_HEADER]) + # Note: The env argument is not needed after the first call within a request context. + # All subsequent calls within a request should return the same GeoNode record. + strong_memoize(:proxied_site) do + signed_data = Gitlab::Geo::SignedData.new + signed_data.decode_data(env[GEO_PROXIED_EXTRA_DATA_HEADER]) - signed_data.geo_node + signed_data.geo_node + end end def self.license_allows? diff --git a/ee/spec/helpers/ee/gitlab_routing_helper_spec.rb b/ee/spec/helpers/ee/gitlab_routing_helper_spec.rb index 962eabab39098e20e4e1cea8055ff4f9188844a2..d22665465d86f238c9c891c45ee43da1aef76252 100644 --- a/ee/spec/helpers/ee/gitlab_routing_helper_spec.rb +++ b/ee/spec/helpers/ee/gitlab_routing_helper_spec.rb @@ -5,6 +5,7 @@ RSpec.describe EE::GitlabRoutingHelper do include ProjectsHelper include ApplicationSettingsHelper + include EE::GeoHelpers let_it_be(:primary, reload: true) do create( @@ -53,6 +54,51 @@ end end + describe '#geo_proxied_http_url_to_repo' do + subject { helper.geo_proxied_http_url_to_repo(primary, repo) } + + let(:repo) { project } + + it { is_expected.to eq('http://localhost:123/relative/foo/bar.git') } + end + + describe '#geo_proxied_ssh_url_to_repo' do + subject { helper.geo_proxied_ssh_url_to_repo(proxied_site, primary_container) } + + let(:proxied_site) { instance_double(GeoNode, uri: URI::HTTP.build(host: 'proxied-host', port: 5678)) } + let(:primary_container) { instance_double(Project, ssh_url_to_repo: ssh_url_to_repo) } + + context 'with default gitlab shell ssh_port' do + let(:ssh_url_to_repo) { 'git@primary:bar.git' } + + before do + stub_config( + gitlab_shell: { + ssh_port: 22, + ssh_host: 'primary' + } + ) + end + + it { is_expected.to eq('git@proxied-host:bar.git') } + end + + context 'with custom gitlab shell ssh_port' do + let(:ssh_url_to_repo) { 'ssh://git@primary:123/bar.git' } + + before do + stub_config( + gitlab_shell: { + ssh_port: 123, + ssh_host: 'primary' + } + ) + end + + it { is_expected.to eq('ssh://git@proxied-host:123/bar.git') } + end + end + describe '#geo_primary_default_url_to_repo' do subject { helper.geo_primary_default_url_to_repo(repo) } diff --git a/ee/spec/helpers/projects_helper_spec.rb b/ee/spec/helpers/projects_helper_spec.rb index 4ed11070bd60a0e7eb8ef8ffc5de98145663df29..dc7073973184e55f138b15357e0e05751ca60852 100644 --- a/ee/spec/helpers/projects_helper_spec.rb +++ b/ee/spec/helpers/projects_helper_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe ProjectsHelper do + include ::EE::GeoHelpers + let_it_be_with_refind(:project) { create(:project) } before do @@ -579,4 +581,34 @@ def dismiss_banner end end end + + describe '#http_clone_url_to_repo' do + let(:geo_url) { 'http://localhost/geonode_url' } + let(:geo_node) { instance_double(GeoNode, url: geo_url) } + + subject { helper.http_clone_url_to_repo(project) } + + before do + stub_proxied_site(geo_node) + + allow(helper).to receive(:geo_proxied_http_url_to_repo).with(geo_node, project).and_return(geo_url) + end + + it { expect(subject).to eq geo_url } + end + + describe '#ssh_clone_url_to_repo' do + let(:geo_url) { 'git@localhost/geonode_url' } + let(:geo_node) { instance_double(GeoNode, url: geo_url) } + + subject { helper.ssh_clone_url_to_repo(project) } + + before do + stub_proxied_site(geo_node) + + allow(helper).to receive(:geo_proxied_ssh_url_to_repo).with(geo_node, project).and_return(geo_url) + end + + it { expect(subject).to eq geo_url } + end end diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 3c8a692b0b3d4f664c1a577ff499fcf8fb8d66c6..978fbddcfe5f3073ff97a83e65787d9b7155941c 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -1384,4 +1384,24 @@ def license_name expect(helper.remote_mirror_setting_enabled?).to be_falsy end end + + describe '#http_clone_url_to_repo' do + before do + allow(project).to receive(:http_url_to_repo).and_return('http_url_to_repo') + end + + subject { helper.http_clone_url_to_repo(project) } + + it { expect(subject).to eq('http_url_to_repo') } + end + + describe '#ssh_clone_url_to_repo' do + before do + allow(project).to receive(:ssh_url_to_repo).and_return('ssh_url_to_repo') + end + + subject { helper.ssh_clone_url_to_repo(project) } + + it { expect(subject).to eq('ssh_url_to_repo') } + end end