diff --git a/ee/lib/api/code_suggestions.rb b/ee/lib/api/code_suggestions.rb index b2f8be7baac52c8df2de1986caf7949b6b670a35..4b51fb7d9ecb542e0ddd15df20a62c48ec7bb76a 100644 --- a/ee/lib/api/code_suggestions.rb +++ b/ee/lib/api/code_suggestions.rb @@ -32,12 +32,12 @@ def active_code_suggestions_purchase?(project_id) end end - def model_gateway_headers(headers) + def model_gateway_headers(headers, gateway_token) telemetry_headers = headers.select { |k| /\Ax-gitlab-cs-/i.match?(k) } { 'X-Gitlab-Authentication-Type' => 'oidc', - 'Authorization' => "Bearer #{headers['X-Gitlab-Oidc-Token']}", + 'Authorization' => "Bearer #{gateway_token}", 'Content-Type' => 'application/json', 'User-Agent' => headers["User-Agent"] # Forward the User-Agent on to the model gateway }.merge(telemetry_headers).transform_values { |v| Array(v) } @@ -99,22 +99,27 @@ def completions_endpoint resources :completions do post do if Gitlab.org_or_com? - not_found! unless ::Feature.enabled?(:code_suggestions_completion_api, current_user) + forbidden! unless ::Feature.enabled?(:code_suggestions_completion_api, current_user) not_found! unless active_code_suggestions_purchase?(params['project_id']) + + token = Gitlab::CodeSuggestions::AccessToken.new( + current_user, + gitlab_realm: gitlab_realm + ).encoded else - not_found! unless ::Feature.enabled?(:self_managed_code_suggestions_completion_api) + forbidden! unless ::Feature.enabled?(:self_managed_code_suggestions_completion_api) code_suggestions_token = ::Ai::ServiceAccessToken.code_suggestions.active.last unauthorized! if code_suggestions_token.nil? - headers['X-Gitlab-Oidc-Token'] = code_suggestions_token.token + token = code_suggestions_token.token end workhorse_headers = Gitlab::Workhorse.send_url( completions_endpoint, body: params.except(:private_token).to_json, - headers: model_gateway_headers(headers), + headers: model_gateway_headers(headers, token), method: "POST" ) diff --git a/ee/spec/requests/api/code_suggestions_spec.rb b/ee/spec/requests/api/code_suggestions_spec.rb index f7b2c85e29c857a72c0985897b84675669e4b4a5..36dc9a78b760e06fa7e4359158060d5ed728cbe6 100644 --- a/ee/spec/requests/api/code_suggestions_spec.rb +++ b/ee/spec/requests/api/code_suggestions_spec.rb @@ -54,6 +54,15 @@ end end + shared_examples 'a forbidden response' do + include_examples 'a response', 'unauthorized' do + let(:result) { :forbidden } + let(:body) do + { "message" => "403 Forbidden" } + end + end + end + shared_examples 'a not found response' do include_examples 'a response', 'not found' do let(:result) { :not_found } @@ -169,7 +178,6 @@ end describe 'POST /code_suggestions/completions' do - let_it_be(:token) { 'JWTTOKEN' } let(:access_code_suggestions) { true } let(:body) do @@ -190,6 +198,7 @@ end before do + allow(Gitlab).to receive(:org_or_com?).and_return(is_saas) allow(Ability).to receive(:allowed?).and_call_original allow(Ability).to receive(:allowed?).with(current_user, :access_code_suggestions, :global) .and_return(access_code_suggestions) @@ -215,7 +224,7 @@ stub_env('CODE_SUGGESTIONS_BASE_URL', nil) end - it 'delegates downstream service call to Workhorse with auth token from the DB' do + it 'delegates downstream service call to Workhorse with correct auth token' do post_api expect(response.status).to be(200) @@ -288,9 +297,8 @@ end context 'when the instance is Gitlab.org_or_com' do - before do - allow(Gitlab).to receive(:org_or_com?).and_return(true) - end + let(:is_saas) { true } + let_it_be(:token) { 'generated-jwt' } let(:headers) do { @@ -301,6 +309,12 @@ } end + before do + allow_next_instance_of(Gitlab::CodeSuggestions::AccessToken) do |instance| + allow(instance).to receive(:encoded).and_return(token) + end + end + context 'when project does not have active code suggestions purchase' do let(:current_user) { create(:user) } @@ -327,7 +341,7 @@ stub_feature_flags(code_suggestions_completion_api: false) end - include_examples 'a not found response' + include_examples 'a forbidden response' end context 'when purchase_code_suggestions feature flag is disabled' do @@ -342,9 +356,9 @@ end context 'when the instance is Gitlab self-managed' do - before do - allow(Gitlab).to receive(:org_or_com?).and_return(false) - end + let(:is_saas) { false } + let_it_be(:token) { 'stored-token' } + let_it_be(:service_access_token) { create(:service_access_token, :code_suggestions, :active, token: token) } let(:headers) do { @@ -354,8 +368,6 @@ } end - let_it_be(:service_access_token) { create(:service_access_token, :code_suggestions, :active, token: token) } - it_behaves_like 'code completions endpoint' context 'when there is no active code suggestions token' do @@ -378,7 +390,7 @@ stub_feature_flags(self_managed_code_suggestions_completion_api: false) end - include_examples 'a not found response' + include_examples 'a forbidden response' end end end