From dba8992cad989ff4717058a1c945db8a2b1f3d0a Mon Sep 17 00:00:00 2001 From: Carla Drago <cdrago@gitlab.com> Date: Wed, 26 Jun 2024 15:41:43 +0000 Subject: [PATCH] Add jwt for jira connect branches This adds a query param to the create branch url of the GitLab for Jira app descriptor. It updates the Jira Connect branches controller to handle redirections to self-managed gitlab instances when a jwt is present in the params. Changelog: fixed --- .../jira_connect/app_descriptor_controller.rb | 6 +- .../jira_connect/branches_controller.rb | 22 +++++-- app/models/jira_connect_installation.rb | 6 ++ .../jira_connect_proxy_create_branch.yml | 9 +++ config/routes/jira_connect.rb | 6 +- doc/administration/settings/jira_cloud_app.md | 2 +- .../app_descriptor_controller_spec.rb | 2 +- .../jira_connect/branches_controller_spec.rb | 58 +++++++++++++++++++ spec/models/jira_connect_installation_spec.rb | 18 ++++++ 9 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 config/feature_flags/gitlab_com_derisk/jira_connect_proxy_create_branch.yml diff --git a/app/controllers/jira_connect/app_descriptor_controller.rb b/app/controllers/jira_connect/app_descriptor_controller.rb index 2e0949a584e89..ea1678ba8f3cc 100644 --- a/app/controllers/jira_connect/app_descriptor_controller.rb +++ b/app/controllers/jira_connect/app_descriptor_controller.rb @@ -130,10 +130,14 @@ def relative_to_base_path(full_path) full_path.sub(/^#{jira_connect_base_path}/, '') end + def create_branch_params + "?issue_key={issue.key}&issue_summary={issue.summary}&jwt={jwt}&addonkey=#{Atlassian::JiraConnect.app_key}" + end + def actions { createBranch: { - templateUrl: "#{new_jira_connect_branch_url}?issue_key={issue.key}&issue_summary={issue.summary}" + templateUrl: "#{route_jira_connect_branches_url}#{create_branch_params}" }, searchConnectedWorkspaces: { templateUrl: search_jira_connect_workspaces_url diff --git a/app/controllers/jira_connect/branches_controller.rb b/app/controllers/jira_connect/branches_controller.rb index 4c1b0d2b208a9..6f57805d23cb2 100644 --- a/app/controllers/jira_connect/branches_controller.rb +++ b/app/controllers/jira_connect/branches_controller.rb @@ -1,14 +1,28 @@ # frozen_string_literal: true -# NOTE: This controller does not inherit from JiraConnect::ApplicationController -# because we don't receive a JWT for this action, so we rely on standard GitLab authentication. -class JiraConnect::BranchesController < ApplicationController - feature_category :integrations +class JiraConnect::BranchesController < JiraConnect::ApplicationController + # before_action :authenticate_user!, only: :new + skip_before_action :verify_atlassian_jwt!, only: :new def new + # move authenticate_user! to a before_action when we remove the jira_connect_proxy_create_branch feature flag + authenticate_user! if Feature.enabled?(:jira_connect_proxy_create_branch, current_user) + @new_branch_data = new_branch_data end + # If the GitLab for Jira Cloud app was installed from the Jira marketplace and points to a self-managed instance, + # we route the user to the self-managed instance, otherwise we redirect to :new + def route + if Feature.enabled?(:jira_connect_proxy_create_branch, current_user) && current_jira_installation.proxy? + redirect_to "#{current_jira_installation.create_branch_url}?#{request.query_string}" + + return + end + + redirect_to "#{new_jira_connect_branch_path}?#{request.query_string}" + end + private def initial_branch_name diff --git a/app/models/jira_connect_installation.rb b/app/models/jira_connect_installation.rb index 915342486c5b6..0da8482fea4f7 100644 --- a/app/models/jira_connect_installation.rb +++ b/app/models/jira_connect_installation.rb @@ -54,6 +54,12 @@ def audience_uninstalled_event_url Gitlab::Utils.append_path(instance_url, jira_connect_events_uninstalled_path) end + def create_branch_url + return unless proxy? + + Gitlab::Utils.append_path(instance_url, new_jira_connect_branch_path) + end + def proxy? instance_url.present? end diff --git a/config/feature_flags/gitlab_com_derisk/jira_connect_proxy_create_branch.yml b/config/feature_flags/gitlab_com_derisk/jira_connect_proxy_create_branch.yml new file mode 100644 index 0000000000000..8742727f4e067 --- /dev/null +++ b/config/feature_flags/gitlab_com_derisk/jira_connect_proxy_create_branch.yml @@ -0,0 +1,9 @@ +--- +name: jira_connect_proxy_create_branch +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/391432 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/149377 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/466462 +milestone: '17.2' +group: group::import and integrate +type: gitlab_com_derisk +default_enabled: false diff --git a/config/routes/jira_connect.rb b/config/routes/jira_connect.rb index 3720fb69b95c1..39276a5cb5388 100644 --- a/config/routes/jira_connect.rb +++ b/config/routes/jira_connect.rb @@ -12,7 +12,11 @@ end resources :subscriptions, only: [:index, :create, :destroy] - resources :branches, only: [:new] + resources :branches, only: [:new] do + collection do + get :route + end + end resources :public_keys, only: :show resources :workspaces, only: [] do diff --git a/doc/administration/settings/jira_cloud_app.md b/doc/administration/settings/jira_cloud_app.md index 4c27e7caf8856..7d2ec1ec19c5a 100644 --- a/doc/administration/settings/jira_cloud_app.md +++ b/doc/administration/settings/jira_cloud_app.md @@ -81,7 +81,7 @@ You can use the official GitLab for Jira Cloud app from the Atlassian Marketplac With this method: - GitLab.com [handles the install and uninstall lifecycle events](#gitlabcom-handling-of-app-lifecycle-events) sent from Jira Cloud and forwards them to your GitLab instance. All data from your self-managed instance is still sent directly to Jira Cloud. -- It's not possible to create branches from Jira Cloud. +- With any version of GitLab prior to 17.2 it is not possible to create branches from Jira Cloud on self-managed instances. For more information, see [issue 391432](https://gitlab.com/gitlab-org/gitlab/-/issues/391432). Alternatively, you might want to [install the GitLab for Jira Cloud app manually](#install-the-gitlab-for-jira-cloud-app-manually) if: diff --git a/spec/controllers/jira_connect/app_descriptor_controller_spec.rb b/spec/controllers/jira_connect/app_descriptor_controller_spec.rb index e23b76146aff9..7902533feae5d 100644 --- a/spec/controllers/jira_connect/app_descriptor_controller_spec.rb +++ b/spec/controllers/jira_connect/app_descriptor_controller_spec.rb @@ -64,7 +64,7 @@ jiraDevelopmentTool: { actions: { createBranch: { - templateUrl: 'http://test.host/-/jira_connect/branches/new?issue_key={issue.key}&issue_summary={issue.summary}' + templateUrl: "http://test.host/-/jira_connect/branches/route?issue_key={issue.key}&issue_summary={issue.summary}&jwt={jwt}&addonkey=#{Atlassian::JiraConnect.app_key}" }, searchConnectedWorkspaces: { templateUrl: 'http://test.host/-/jira_connect/workspaces/search' diff --git a/spec/controllers/jira_connect/branches_controller_spec.rb b/spec/controllers/jira_connect/branches_controller_spec.rb index 1d3ddc2e33b9f..117e1cd280282 100644 --- a/spec/controllers/jira_connect/branches_controller_spec.rb +++ b/spec/controllers/jira_connect/branches_controller_spec.rb @@ -44,4 +44,62 @@ end end end + + describe '#route' do + let(:addonkey) { 'app_key' } + let(:params) { { issue_key: 'ACME-123', issue_summary: 'My Issue !@#$%', jwt: jwt, addonkey: addonkey } } + + context 'without a valid jwt' do + let(:jwt) { nil } + + it 'returns 403' do + get :route, params: params + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'with a valid jwt' do + let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'https://self-managed.gitlab.io') } + let(:qsh) { Atlassian::Jwt.create_query_string_hash('https://gitlab.test/subscriptions', 'GET', 'https://gitlab.test') } + let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret) } + let(:symmetric_jwt) { Atlassian::JiraConnect::Jwt::Symmetric.new(jwt) } + let(:query_string) { URI.encode_www_form(params.sort.to_h) } + + before do + allow(Atlassian::JiraConnect::Jwt::Symmetric).to receive(:route).with(params[:jwt]).and_return(symmetric_jwt) + end + + context 'when the jira installation is not for a self-managed instance' do + let_it_be(:installation) { create(:jira_connect_installation) } + + it 'redirects to :new' do + get :route, params: params + expect(response).to redirect_to("#{new_jira_connect_branch_url}?#{query_string}") + end + end + + context 'when the jira installation is for a self-managed instance' do + let(:create_branch_url) do + Gitlab::Utils.append_path(installation.instance_url, new_jira_connect_branch_path) + end + + it 'redirects to the self-managed installation' do + get :route, params: params + expect(response).to redirect_to("#{create_branch_url}?#{query_string}") + end + end + + context 'when jira_connect_proxy_create_branch feature is disabled' do + before do + stub_feature_flags(jira_connect_proxy_create_branch: false) + end + + it 'redirects to :new' do + get :route, params: params + expect(response).to redirect_to("#{new_jira_connect_branch_url}?#{query_string}") + end + end + end + end end diff --git a/spec/models/jira_connect_installation_spec.rb b/spec/models/jira_connect_installation_spec.rb index 6cd1534c0c841..7a50cee768d78 100644 --- a/spec/models/jira_connect_installation_spec.rb +++ b/spec/models/jira_connect_installation_spec.rb @@ -130,6 +130,24 @@ end end + describe 'create_branch_url' do + context 'when the jira installation is not for a self-managed instance' do + let(:installation) { build(:jira_connect_installation) } + + subject(:create_branch) { installation.create_branch_url } + + it { is_expected.to eq(nil) } + end + + context 'when the jira installation is for a self-managed instance' do + let(:installation) { build(:jira_connect_installation, instance_url: 'https://example.com') } + + subject(:create_branch) { installation.create_branch_url } + + it { is_expected.to eq('https://example.com/-/jira_connect/branches/new') } + end + end + describe 'proxy?' do let(:installation) { build(:jira_connect_installation) } -- GitLab