diff --git a/ee/app/controllers/admin/gitlab_duo/model_selection_controller.rb b/ee/app/controllers/admin/gitlab_duo/model_selection_controller.rb new file mode 100644 index 0000000000000000000000000000000000000000..2622f95348f10a4c5a241a58d6cb6a834de6dddd --- /dev/null +++ b/ee/app/controllers/admin/gitlab_duo/model_selection_controller.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# EE:Self Managed +module Admin + module GitlabDuo + class ModelSelectionController < Admin::ApplicationController + feature_category :ai_abstraction_layer + urgency :low + + before_action :ensure_feature_enabled! + + def index; end + + private + + def ensure_feature_enabled! + render_404 unless Ability.allowed?(current_user, :manage_instance_level_model_selection) + end + end + end +end diff --git a/ee/app/policies/ee/global_policy.rb b/ee/app/policies/ee/global_policy.rb index 62e17320d00be8d64bc335df8e0818ec4c9bab19..8307e33cdffe439e86c844aeab8d75f1ed65a36d 100644 --- a/ee/app/policies/ee/global_policy.rb +++ b/ee/app/policies/ee/global_policy.rb @@ -115,6 +115,13 @@ module GlobalPolicy ::GitlabSubscriptions::AddOnPurchase.for_self_managed.for_duo_enterprise.active.exists? end + condition(:user_alowed_to_manage_instance_level_model_selection) do + next false unless ::Feature.enabled?(:instance_level_model_selection) + next false if ::Gitlab::Saas.feature_available?(:gitlab_com_subscriptions) + + ::License.current&.online_cloud_license? + end + condition(:x_ray_available) do next true if ::Gitlab::Saas.feature_available?(:code_suggestions_x_ray) @@ -172,6 +179,10 @@ module GlobalPolicy enable :manage_self_hosted_models_settings end + rule { admin & user_alowed_to_manage_instance_level_model_selection }.policy do + enable :manage_instance_level_model_selection + end + rule { admin & custom_roles_allowed }.policy do enable :admin_member_role enable :view_member_roles diff --git a/ee/app/views/admin/gitlab_duo/model_selection/index.html.haml b/ee/app/views/admin/gitlab_duo/model_selection/index.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..172db6a5df68d73d1323314098e1700ddef2eebe --- /dev/null +++ b/ee/app/views/admin/gitlab_duo/model_selection/index.html.haml @@ -0,0 +1,5 @@ +- page_title s_("AdminModelSelection|GitLab Model Selection") +- add_to_breadcrumbs _("GitLab Duo"), admin_gitlab_duo_path +- add_page_specific_style 'page_bundles/settings' + +#js-admin-model-selection diff --git a/ee/config/routes/admin.rb b/ee/config/routes/admin.rb index 4c355dcc240ce22150fa64dae660e98039ca9d5d..399f11fc81b90c56011aaf16b1e161f884ac9b7f 100644 --- a/ee/config/routes/admin.rb +++ b/ee/config/routes/admin.rb @@ -50,12 +50,14 @@ resources :seat_utilization, only: [:index] resources :configuration, only: [:index] resources :self_hosted, only: [:index] + resources :model_selection, only: [:index] scope :usage do get '/', action: :index, controller: 'usage', as: :usage end get 'self_hosted(/*vueroute)', to: 'self_hosted#index' + get 'model_selection', to: 'model_selection#index' end get '/code_suggestions', to: redirect('admin/gitlab_duo/seat_utilization') diff --git a/ee/spec/policies/global_policy_spec.rb b/ee/spec/policies/global_policy_spec.rb index 87d68c18462dc58e54db8bb9d6f6903910252587..f13ec4220a3dea4f4525ffb413445702c7d6164c 100644 --- a/ee/spec/policies/global_policy_spec.rb +++ b/ee/spec/policies/global_policy_spec.rb @@ -964,6 +964,64 @@ end end + describe 'manage instance level model selection' do + context 'when user is not an admin' do + it { is_expected.to be_disallowed(:manage_instance_level_model_selection) } + end + + context 'when user is an admin', :enable_admin_mode do + let(:current_user) { admin } + + context 'when feature flag is disabled' do + before do + stub_feature_flags(instance_level_model_selection: false) + end + + it { is_expected.to be_disallowed(:manage_instance_level_model_selection) } + end + + context 'when feature flag is enabled' do + before do + stub_feature_flags(instance_level_model_selection: true) + end + + context 'when on SaaS' do + before do + stub_saas_features(gitlab_com_subscriptions: true) + end + + it { is_expected.to be_disallowed(:manage_instance_level_model_selection) } + end + + context 'when not on SaaS' do + before do + stub_saas_features(gitlab_com_subscriptions: false) + end + + context 'when no online license found' do + let(:license) { build(:license, cloud: false) } + + before do + allow(License).to receive(:current).and_return(license) + end + + it { is_expected.to be_disallowed(:manage_instance_level_model_selection) } + end + + context 'when online license is found' do + let(:license) { build(:license, cloud: true) } + + before do + allow(License).to receive(:current).and_return(license) + end + + it { is_expected.to be_allowed(:manage_instance_level_model_selection) } + end + end + end + end + end + describe 'manage duo core features' do let_it_be(:license) { create(:license, plan: License::ULTIMATE_PLAN) } let(:current_user) { admin } diff --git a/ee/spec/requests/admin/gitlab_duo/model_selection_controller_spec.rb b/ee/spec/requests/admin/gitlab_duo/model_selection_controller_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..564cd10874051599b35b61a00e774ea904d9e0fd --- /dev/null +++ b/ee/spec/requests/admin/gitlab_duo/model_selection_controller_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Admin::GitlabDuo::ModelSelectionController, :enable_admin_mode, feature_category: :ai_abstraction_layer do + let(:admin) { create(:admin) } + let(:duo_features_enabled) { true } + let_it_be(:license) { create(:license, plan: License::ULTIMATE_PLAN) } + let_it_be(:add_on_purchase) do + create(:gitlab_subscription_add_on_purchase, :duo_enterprise, :active, :self_managed) + end + + subject(:get_index) { get admin_gitlab_duo_model_selection_index_path } + + before do + sign_in(admin) + end + + context 'when user is not authorized' do + it 'returns 404' do + allow(Ability).to receive(:allowed?).and_call_original + expect(Ability).to receive(:allowed?).with(admin, :manage_instance_level_model_selection).and_return(false) + + get_index + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when user is authorized' do + let(:license) { build(:license, cloud: true) } + + before do + stub_saas_features(gitlab_com_subscriptions: false) + stub_feature_flags(instance_level_model_selection: true) + allow(License).to receive(:current).and_return(license) + + sign_in(admin) + end + + it 'returns 200' do + allow(Ability).to receive(:allowed?).and_call_original + expect(Ability).to receive(:allowed?).with(admin, :manage_instance_level_model_selection).and_return(true) + + get_index + + expect(response).to have_gitlab_http_status(:ok) + end + end +end