diff --git a/ee/app/models/ee/user_preference.rb b/ee/app/models/ee/user_preference.rb index a4ecacf366a43d6ef506da714692782c1af46d26..886ddc2fda010b02522c7c8842e24871007f0619 100644 --- a/ee/app/models/ee/user_preference.rb +++ b/ee/app/models/ee/user_preference.rb @@ -5,7 +5,10 @@ module UserPreference extend ActiveSupport::Concern prepended do + extend ::Gitlab::Utils::Override + belongs_to :default_duo_add_on_assignment, class_name: 'GitlabSubscriptions::UserAddOnAssignment', optional: true + belongs_to :duo_default_namespace, class_name: 'Namespace', optional: true validates :roadmap_epics_state, allow_nil: true, inclusion: { in: ::Epic.available_states.values, message: "%{value} is not a valid epic state id" @@ -14,12 +17,25 @@ module UserPreference validates :epic_notes_filter, inclusion: { in: ::UserPreference::NOTES_FILTERS.values }, presence: true validate :check_seat_for_default_duo_assigment, if: :default_duo_add_on_assignment_id_changed? + validate :validate_duo_default_namespace_id, if: :duo_default_namespace_id_changed? validates :policy_advanced_editor, allow_nil: false, inclusion: { in: [true, false] } attribute :policy_advanced_editor, default: false + def duo_default_namespace_candidates(limit: nil) + if ::Gitlab::Saas.feature_available?(:gitlab_duo_saas_only) + distinct_eligible_duo_add_on_assignments.limit(limit).map(&:namespace) + else + namespaces = user.authorized_groups.top_level.limit(limit).to_a + namespaces.append(user.namespace) if user.namespace + namespaces + end + end + def eligible_duo_add_on_assignments + return unless ::Gitlab::Saas.feature_available?(:gitlab_com_subscriptions) + assignable_enum_value = ::GitlabSubscriptions::AddOn.names.values_at( *::GitlabSubscriptions::AddOn::SEAT_ASSIGNABLE_DUO_ADD_ONS ) @@ -38,6 +54,32 @@ def distinct_eligible_duo_add_on_assignments eligible_duo_add_on_assignments.select(distinct_query) end + def no_eligible_duo_add_on_assignments? + eligible_duo_add_on_assignments.none? + end + + def get_default_duo_namespace + if ::Gitlab::Saas.feature_available?(:gitlab_duo_saas_only) + return default_duo_add_on_assignment.namespace if default_duo_add_on_assignment.present? + elsif duo_default_namespace.present? + return duo_default_namespace + end + + candidate_namespaces = duo_default_namespace_candidates(limit: 2) + + return if candidate_namespaces.size != 1 + + candidate_namespaces.first + end + + override :duo_default_namespace + def duo_default_namespace + namespace = super + namespace if namespace && user.can?(:read_namespace, namespace) + end + + private + def check_seat_for_default_duo_assigment return if default_duo_add_on_assignment_id.nil? @@ -47,18 +89,11 @@ def check_seat_for_default_duo_assigment "No Duo seat assignments with namespace found with ID #{default_duo_add_on_assignment_id}") end - def no_eligible_duo_add_on_assignments? - eligible_duo_add_on_assignments.none? - end - - def get_default_duo_namespace - return default_duo_add_on_assignment.namespace if default_duo_add_on_assignment.present? - - assignments = distinct_eligible_duo_add_on_assignments.limit(2).to_a - - return if assignments.size != 1 + def validate_duo_default_namespace_id + return unless duo_default_namespace_id || duo_default_namespace + return if user.user_preference.duo_default_namespace_candidates.include?(duo_default_namespace) - assignments.first.add_on_purchase.namespace + errors.add(:duo_default_namespace_id, "The given namespace is not among the eligible namespaces") end end end diff --git a/ee/app/policies/ee/user_policy.rb b/ee/app/policies/ee/user_policy.rb index 4ab64b67a73e6f18b560aeb33fe931ba0dbcd575..c1a967ba843a14a9ded567e3fce36e9fb7e8804e 100644 --- a/ee/app/policies/ee/user_policy.rb +++ b/ee/app/policies/ee/user_policy.rb @@ -63,19 +63,23 @@ def private_profile? end def can_assign_default_duo_group? - return false unless ::Gitlab::Saas.feature_available?(:gitlab_com_subscriptions) + if ::Gitlab::Saas.feature_available?(:gitlab_com_subscriptions) + return false unless ::Feature.enabled?(:ai_user_default_duo_namespace, user) - return false unless ::Feature.enabled?(:ai_user_default_duo_namespace, user) + users_namespaces = user.user_preference.duo_default_namespace_candidates(limit: 2) - users_namespaces = user.user_preference.distinct_eligible_duo_add_on_assignments.map(&:namespace) + return false if users_namespaces.empty? - has_feature_enabled = users_namespaces.any? do |namespace| - ::Feature.enabled?(:ai_model_switching, namespace) - end + ::Gitlab::CurrentSettings.current_application_settings.duo_features_enabled + else + return false unless user.allowed_to_use?(:access_duo_agentic_chat) + + users_namespaces = user.user_preference.duo_default_namespace_candidates - return false unless has_feature_enabled + return false if users_namespaces.empty? - ::Gitlab::CurrentSettings.current_application_settings.duo_features_enabled + true + end end end end