diff --git a/app/controllers/jira_connect/application_controller.rb b/app/controllers/jira_connect/application_controller.rb index 9e55aace783263676c9b1088eb2683653c9ab44e..e26d69314cdad629ce6d192eb0a15084f7dd439b 100644 --- a/app/controllers/jira_connect/application_controller.rb +++ b/app/controllers/jira_connect/application_controller.rb @@ -3,12 +3,6 @@ class JiraConnect::ApplicationController < ApplicationController include Gitlab::Utils::StrongMemoize - CORS_ALLOWED_METHODS = { - '/-/jira_connect/oauth_application_id' => %i[GET OPTIONS], - '/-/jira_connect/subscriptions.json' => %i[GET OPTIONS], - '/-/jira_connect/subscriptions/*' => %i[DELETE OPTIONS] - }.freeze - skip_before_action :authenticate_user! skip_before_action :verify_authenticity_token before_action :verify_atlassian_jwt! @@ -66,25 +60,4 @@ def jwt def auth_token params[:jwt] || request.headers['Authorization']&.split(' ', 2)&.last end - - def cors_allowed_methods - CORS_ALLOWED_METHODS[resource] - end - - def resource - request.path.gsub(%r{/\d+$}, '/*') - end - - def set_cors_headers - return unless allow_cors_request? - - response.set_header('Access-Control-Allow-Origin', Gitlab::CurrentSettings.jira_connect_proxy_url) - response.set_header('Access-Control-Allow-Methods', cors_allowed_methods.join(', ')) - end - - def allow_cors_request? - return false if cors_allowed_methods.nil? - - !Gitlab.com? && Gitlab::CurrentSettings.jira_connect_proxy_url.present? - end end diff --git a/app/controllers/jira_connect/cors_preflight_checks_controller.rb b/app/controllers/jira_connect/cors_preflight_checks_controller.rb deleted file mode 100644 index 3f30c1e04df44829b100b5bf120222b7c8f8f0c9..0000000000000000000000000000000000000000 --- a/app/controllers/jira_connect/cors_preflight_checks_controller.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -module JiraConnect - class CorsPreflightChecksController < ApplicationController - feature_category :integrations - - skip_before_action :verify_atlassian_jwt! - before_action :set_cors_headers - - def index - return render_404 unless allow_cors_request? - - render plain: '', content_type: 'text/plain' - end - end -end diff --git a/app/controllers/jira_connect/oauth_application_ids_controller.rb b/app/controllers/jira_connect/oauth_application_ids_controller.rb index 48827785e56be3d457ed4780056c09bb0a93c392..de520337af3f9c5a691041c5e179b7d40ef63c8e 100644 --- a/app/controllers/jira_connect/oauth_application_ids_controller.rb +++ b/app/controllers/jira_connect/oauth_application_ids_controller.rb @@ -5,7 +5,6 @@ class OauthApplicationIdsController < ApplicationController feature_category :integrations skip_before_action :verify_atlassian_jwt! - before_action :set_cors_headers def show if show_application_id? diff --git a/app/controllers/jira_connect/subscriptions_controller.rb b/app/controllers/jira_connect/subscriptions_controller.rb index da4b25943f730a09ee0f0eccce88835b70e71f93..30cfeb563eb97944264d35ef42b3cf34352182f8 100644 --- a/app/controllers/jira_connect/subscriptions_controller.rb +++ b/app/controllers/jira_connect/subscriptions_controller.rb @@ -23,7 +23,6 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController push_frontend_feature_flag(:jira_connect_oauth_self_managed, @user) end - before_action :set_cors_headers before_action :allow_rendering_in_iframe, only: :index before_action :verify_qsh_claim!, only: :index before_action :allow_self_managed_content_security_policy, only: :index diff --git a/config/application.rb b/config/application.rb index 249db9c6a67b1014831c00fedba4ebedc2a5ac4c..56eabc59815c9a70b508b7c4d2b020199501a8cf 100644 --- a/config/application.rb +++ b/config/application.rb @@ -417,6 +417,21 @@ class Application < Rails::Application expose: headers_to_expose end + allow do + origins { |source, env| source == Gitlab::CurrentSettings.jira_connect_proxy_url } + resource '/-/jira_connect/oauth_application_id', headers: :any, credentials: false, methods: %i(get options) + end + + allow do + origins { |source, env| source == Gitlab::CurrentSettings.jira_connect_proxy_url } + resource '/-/jira_connect/subscriptions.json', headers: :any, credentials: false, methods: %i(get options) + end + + allow do + origins { |source, env| source == Gitlab::CurrentSettings.jira_connect_proxy_url } + resource '/-/jira_connect/subscriptions/*', headers: :any, credentials: false, methods: %i(delete options) + end + # Cross-origin requests must be enabled for the Authorization code with PKCE OAuth flow when used from a browser. %w(/oauth/token /oauth/revoke).each do |oauth_path| allow do diff --git a/config/routes.rb b/config/routes.rb index e218099ec4a2f0690ab109e7a73d04acd3688cf9..3482b0566ce8863ad0014458e373234e87bf6cef 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -55,10 +55,9 @@ match '/oauth/token' => 'oauth/tokens#create', via: :options match '/oauth/revoke' => 'oauth/tokens#revoke', via: :options - match '/-/jira_connect/oauth_application_id' => 'jira_connect/cors_preflight_checks#index', via: :options - match '/-/jira_connect/subscriptions.json' => 'jira_connect/cors_preflight_checks#index', via: :options - match '/-/jira_connect/subscriptions/:id' => 'jira_connect/cors_preflight_checks#index', via: :options - match '/-/jira_connect/installations' => 'jira_connect/cors_preflight_checks#index', via: :options + match '/-/jira_connect/oauth_application_id' => 'jira_connect/oauth_application_ids#show', via: :options + match '/-/jira_connect/subscriptions(.:format)' => 'jira_connect/subscriptions#index', via: :options + match '/-/jira_connect/subscriptions/:id' => 'jira_connect/subscriptions#delete', via: :options # Sign up scope path: '/users/sign_up', module: :registrations, as: :users_sign_up do diff --git a/spec/requests/jira_connect/cors_preflight_checks_controller_spec.rb b/spec/requests/jira_connect/cors_preflight_checks_controller_spec.rb deleted file mode 100644 index e356aaa3796518fd282ae1ebbc350ed41ade475d..0000000000000000000000000000000000000000 --- a/spec/requests/jira_connect/cors_preflight_checks_controller_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe JiraConnect::CorsPreflightChecksController do - shared_examples 'allows cross-origin requests on self managed' do - it 'renders not found' do - options path - - expect(response).to have_gitlab_http_status(:not_found) - expect(response.headers['Access-Control-Allow-Origin']).to be_nil - end - - context 'with jira_connect_proxy_url setting' do - before do - stub_application_setting(jira_connect_proxy_url: 'https://gitlab.com') - - options path, headers: { 'Origin' => 'http://notgitlab.com' } - end - - it 'returns 200' do - expect(response).to have_gitlab_http_status(:ok) - end - - it 'responds with access-control-allow headers', :aggregate_failures do - expect(response.headers['Access-Control-Allow-Origin']).to eq 'https://gitlab.com' - expect(response.headers['Access-Control-Allow-Methods']).to eq allowed_methods - expect(response.headers['Access-Control-Allow-Credentials']).to be_nil - end - - context 'when on GitLab.com' do - before do - allow(Gitlab).to receive(:com?).and_return(true) - end - - it 'renders not found' do - options path - - expect(response).to have_gitlab_http_status(:not_found) - expect(response.headers['Access-Control-Allow-Origin']).to be_nil - end - end - end - end - - describe 'OPTIONS /-/jira_connect/oauth_application_id' do - let(:allowed_methods) { 'GET, OPTIONS' } - let(:path) { '/-/jira_connect/oauth_application_id' } - - it_behaves_like 'allows cross-origin requests on self managed' - end - - describe 'OPTIONS /-/jira_connect/subscriptions.json' do - let(:allowed_methods) { 'GET, OPTIONS' } - let(:path) { '/-/jira_connect/subscriptions.json' } - - it_behaves_like 'allows cross-origin requests on self managed' - end - - describe 'OPTIONS /-/jira_connect/subscriptions/:id' do - let(:allowed_methods) { 'DELETE, OPTIONS' } - let(:path) { '/-/jira_connect/subscriptions/123' } - - it_behaves_like 'allows cross-origin requests on self managed' - end -end diff --git a/spec/requests/jira_connect/oauth_application_ids_controller_spec.rb b/spec/requests/jira_connect/oauth_application_ids_controller_spec.rb index 4b3467059ab596445e9a046826f9e0af9416a4c9..ed619bc50a77de88da3c0a084acff26afdfbd941 100644 --- a/spec/requests/jira_connect/oauth_application_ids_controller_spec.rb +++ b/spec/requests/jira_connect/oauth_application_ids_controller_spec.rb @@ -4,7 +4,7 @@ RSpec.describe JiraConnect::OauthApplicationIdsController do describe 'GET /-/jira_connect/oauth_application_id' do - let(:cors_request_headers) { { 'Origin' => 'http://notgitlab.com' } } + let(:cors_request_headers) { { 'Origin' => 'https://gitlab.com' } } before do stub_application_setting(jira_connect_application_key: '123456') @@ -50,4 +50,21 @@ end end end + + describe 'OPTIONS /-/jira_connect/oauth_application_id' do + let(:cors_request_headers) { { 'Origin' => 'https://gitlab.com', 'access-control-request-method' => 'GET' } } + + before do + stub_application_setting(jira_connect_application_key: '123456') + stub_application_setting(jira_connect_proxy_url: 'https://gitlab.com') + end + + it 'allows cross-origin requests', :aggregate_failures do + options '/-/jira_connect/oauth_application_id', headers: cors_request_headers + + expect(response.headers['Access-Control-Allow-Origin']).to eq 'https://gitlab.com' + expect(response.headers['Access-Control-Allow-Methods']).to eq 'GET, OPTIONS' + expect(response.headers['Access-Control-Allow-Credentials']).to be_nil + end + end end diff --git a/spec/requests/jira_connect/subscriptions_controller_spec.rb b/spec/requests/jira_connect/subscriptions_controller_spec.rb index ba195471a12c7526316bf1e4cdae847e3b3f19df..73a825d471ae9bce9dfe82f0a733c80e328e4fa1 100644 --- a/spec/requests/jira_connect/subscriptions_controller_spec.rb +++ b/spec/requests/jira_connect/subscriptions_controller_spec.rb @@ -10,7 +10,7 @@ end let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret) } - let(:cors_request_headers) { { 'Origin' => 'http://notgitlab.com' } } + let(:cors_request_headers) { { 'Origin' => 'https://gitlab.com' } } let(:path) { '/-/jira_connect/subscriptions' } let(:params) { { jwt: jwt } } @@ -49,6 +49,38 @@ end end + describe 'OPTIONS /-/jira_connect/subscriptions' do + let(:cors_request_headers) { { 'Origin' => 'https://gitlab.com', 'access-control-request-method' => 'GET' } } + + before do + stub_application_setting(jira_connect_proxy_url: 'https://gitlab.com') + end + + it 'allows cross-origin requests', :aggregate_failures do + options '/-/jira_connect/subscriptions.json', headers: cors_request_headers + + expect(response.headers['Access-Control-Allow-Origin']).to eq 'https://gitlab.com' + expect(response.headers['Access-Control-Allow-Methods']).to eq 'GET, OPTIONS' + expect(response.headers['Access-Control-Allow-Credentials']).to be_nil + end + end + + describe 'OPTIONS /-/jira_connect/subscriptions/:id' do + let(:cors_request_headers) { { 'Origin' => 'https://gitlab.com', 'access-control-request-method' => 'DELETE' } } + + before do + stub_application_setting(jira_connect_proxy_url: 'https://gitlab.com') + end + + it 'allows cross-origin requests', :aggregate_failures do + options '/-/jira_connect/subscriptions/1', headers: cors_request_headers + + expect(response.headers['Access-Control-Allow-Origin']).to eq 'https://gitlab.com' + expect(response.headers['Access-Control-Allow-Methods']).to eq 'DELETE, OPTIONS' + expect(response.headers['Access-Control-Allow-Credentials']).to be_nil + end + end + describe 'DELETE /-/jira_connect/subscriptions/:id' do let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'http://self-managed-gitlab.com') } let_it_be(:subscription) { create(:jira_connect_subscription, installation: installation) } @@ -58,7 +90,7 @@ end let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret) } - let(:cors_request_headers) { { 'Origin' => 'http://notgitlab.com' } } + let(:cors_request_headers) { { 'Origin' => 'https://gitlab.com' } } let(:params) { { jwt: jwt, format: :json } } before do