<commit_before>class MiqUserRole < ActiveRecord::Base
  has_many                :miq_groups, :dependent => :restrict_with_exception
  has_and_belongs_to_many :miq_product_features, :join_table => :miq_roles_features

  virtual_column :group_count,                      :type => :integer
  virtual_column :vm_restriction,                   :type => :string

  validates_presence_of   :name
  validates_uniqueness_of :name

  serialize :settings

  default_value_for :read_only, false

  before_destroy { |r| raise "Read only roles cannot be deleted." if r.read_only }

  include ReportableMixin

  FIXTURE_DIR  = File.join(Rails.root, "db/fixtures")
  FIXTURE_PATH = File.join(FIXTURE_DIR, self.table_name)
  FIXTURE_YAML = "#{FIXTURE_PATH}.yml"

  SCOPES = [:base, :one, :sub ]

  RESTRICTIONS = {
    :user          => "Only User Owned",
    :user_or_group => "Only User or Group Owned"
  }

  def feature_identifiers
    self.miq_product_features.collect(&:identifier)
  end

  def allows?(options={})
    ident = options[:identifier]
    raise "No value provided for option :identifier" if ident.nil?

    if ident.kind_of?(MiqProductFeature)
      feat = ident
      ident = feat.identifier
    end

    return true if self.feature_identifiers.include?(ident)

    return false unless MiqProductFeature.feature_exists?(ident)

    parent = MiqProductFeature.feature_parent(ident)
    return false if parent.nil?

    return self.allows?(:identifier => parent)
  end

  def self.allows?(role, options={})
    role = self.get_role(role)
    return false if role.nil?
    role.allows?(options)
  end

  def allows_any?(options={})
    scope = options[:scope] || :sub
    raise ":scope must be one of #{SCOPES.inspect}" unless SCOPES.include?(scope)

    idents = options[:identifiers].to_miq_a
    return false if idents.empty?

    if [:base, :sub].include?(scope)
      # Check passed in identifiers
      return true if idents.any? {|i| self.allows?(:identifier => i)}
    end

    return false if scope == :base

    # Check children of passed in identifiers (scopes :one and :base)
    return idents.any? {|i| self.allows_any_children?(:scope => (scope == :one ? :base : :sub), :identifier => i)}
  end

  def self.allows_any?(role, options={})
    role = self.get_role(role)
    return false if role.nil?
    role.allows_any?(options)
  end

  def allows_all?(options={})
    scope = options[:scope] || :sub
    raise ":scope must be one of #{SCOPES.inspect}" unless SCOPES.include?(scope)

    options[:scope] ||= :sub
    idents = options[:identifiers].to_miq_a
    return false if idents.empty?

    if [:base, :sub].include?(scope)
      # Check passed in identifiers
      return true if idents.all? {|i| self.allows?(:identifier => i)}
    end
    return false if scope == :base

    # Check children of passed in identifiers (scopes :one and :base)
    return idents.all? {|i| self.allows_all_children?(:scope => (scope == :one ? :base : :sub), :identifier => i)}
  end

  def self.allows_all?(role, options={})
    role = self.get_role(role)
    return false if role.nil?
    role.allows_all?(options)
  end

  def allows_any_children?(options={})
    ident = options.delete(:identifier)
    return false if ident.nil? || !MiqProductFeature.feature_exists?(ident)

    child_idents = MiqProductFeature.feature_children(ident)
    self.allows_any?(options.merge(:identifiers => child_idents))
  end

  def self.allows_any_children?(role, options={})
    role = self.get_role(role)
    return false if role.nil?
    role.allows_any_children?(options)
  end

  def allows_all_children?(options={})
    ident = options.delete(:identifier)
    return false if ident.nil? || !MiqProductFeature.feature_exists?(ident)

    child_idents = MiqProductFeature.feature_children(ident)
    self.allows_all?(options.merge(:identifiers => child_idents))
  end

  def self.allows_all_children?(role, options={})
    role = self.get_role(role)
    return false if role.nil?
    role.allows_all_children?(options)
  end

  def self.get_role(role)
    case role
    when self, nil
      role
    when Integer
      includes(:miq_product_features).find_by_id(role)
    else
      includes(:miq_product_features).find_by_name(role)
    end
  end

  def self_service_role?
    [:user_or_group, :user].include?((self.settings || {}).fetch_path(:restrictions, :vms))
  end

  def limited_self_service_role?
    (self.settings || {}).fetch_path(:restrictions, :vms) == :user
  end

  def self.seed
    MiqRegion.my_region.lock do
      self.seed_from_array(YAML.load_file(FIXTURE_YAML))

      Dir.glob(File.join(FIXTURE_PATH, "*.yml")).each do |fixture|
        self.seed_from_array(YAML.load_file(fixture), true)
      end
    end
  end

  def self.seed_from_array(array, merge_features = false)
    new_roles = []
    array.each do |hash|
      feature_ids = hash.delete(:miq_product_feature_identifiers)

      hash[:miq_product_features] = MiqProductFeature.where(:identifier => feature_ids).to_a
      role = self.find_by_name(hash[:name]) || self.new(hash.except(:id))
      new_role = role.new_record?
      hash[:miq_product_features] &&= role.miq_product_features if !new_role && merge_features
      unless role.settings.nil? # Makse sure existing settings are merged in with the new ones.
        new_settings = hash.delete(:settings) || {}
        role.settings.merge!(new_settings)
      end
      role.update_attributes(hash.except(:id))

      new_roles << role if new_role
    end

    self.migrate_roles(new_roles)
  end

  def self.migrate_roles(roles)
    roles.each do |role|
      # Migrate settings stored in UiTaskSet#set_data to settings
      old_role_name = role.name.split("-").last
      old_role = UiTaskSet.in_my_region.find_by_name(old_role_name)
      unless old_role.nil? || old_role.set_data.blank?
        role.settings = {:report_menus => old_role.set_data}
        role.save
      end

      # Migrate widgets that may reference old role name
      widgets ||= MiqWidget.in_my_region
      widgets.each do |w|
        if w.visibility.kind_of?(Hash) && w.visibility.has_key?(:roles) && w.visibility[:roles].include?(old_role_name)
          idx = w.visibility[:roles].index(old_role_name)
          w.visibility[:roles][idx] = role.name
          w.update_attribute(:visibility, w.visibility)
        end
      end
    end
  end

  def group_count
    self.miq_groups.count
  end

  def vm_restriction<commit_msg>Remove legacy live-migration of old UiTaskSets and MiqWidgets.<commit_after>class MiqUserRole < ActiveRecord::Base
  has_many                :miq_groups, :dependent => :restrict_with_exception
  has_and_belongs_to_many :miq_product_features, :join_table => :miq_roles_features

  virtual_column :group_count,                      :type => :integer
  virtual_column :vm_restriction,                   :type => :string

  validates_presence_of   :name
  validates_uniqueness_of :name

  serialize :settings

  default_value_for :read_only, false

  before_destroy { |r| raise "Read only roles cannot be deleted." if r.read_only }

  include ReportableMixin

  FIXTURE_DIR  = File.join(Rails.root, "db/fixtures")
  FIXTURE_PATH = File.join(FIXTURE_DIR, self.table_name)
  FIXTURE_YAML = "#{FIXTURE_PATH}.yml"

  SCOPES = [:base, :one, :sub ]

  RESTRICTIONS = {
    :user          => "Only User Owned",
    :user_or_group => "Only User or Group Owned"
  }

  def feature_identifiers
    self.miq_product_features.collect(&:identifier)
  end

  def allows?(options={})
    ident = options[:identifier]
    raise "No value provided for option :identifier" if ident.nil?

    if ident.kind_of?(MiqProductFeature)
      feat = ident
      ident = feat.identifier
    end

    return true if self.feature_identifiers.include?(ident)

    return false unless MiqProductFeature.feature_exists?(ident)

    parent = MiqProductFeature.feature_parent(ident)
    return false if parent.nil?

    return self.allows?(:identifier => parent)
  end

  def self.allows?(role, options={})
    role = self.get_role(role)
    return false if role.nil?
    role.allows?(options)
  end

  def allows_any?(options={})
    scope = options[:scope] || :sub
    raise ":scope must be one of #{SCOPES.inspect}" unless SCOPES.include?(scope)

    idents = options[:identifiers].to_miq_a
    return false if idents.empty?

    if [:base, :sub].include?(scope)
      # Check passed in identifiers
      return true if idents.any? {|i| self.allows?(:identifier => i)}
    end

    return false if scope == :base

    # Check children of passed in identifiers (scopes :one and :base)
    return idents.any? {|i| self.allows_any_children?(:scope => (scope == :one ? :base : :sub), :identifier => i)}
  end

  def self.allows_any?(role, options={})
    role = self.get_role(role)
    return false if role.nil?
    role.allows_any?(options)
  end

  def allows_all?(options={})
    scope = options[:scope] || :sub
    raise ":scope must be one of #{SCOPES.inspect}" unless SCOPES.include?(scope)

    options[:scope] ||= :sub
    idents = options[:identifiers].to_miq_a
    return false if idents.empty?

    if [:base, :sub].include?(scope)
      # Check passed in identifiers
      return true if idents.all? {|i| self.allows?(:identifier => i)}
    end
    return false if scope == :base

    # Check children of passed in identifiers (scopes :one and :base)
    return idents.all? {|i| self.allows_all_children?(:scope => (scope == :one ? :base : :sub), :identifier => i)}
  end

  def self.allows_all?(role, options={})
    role = self.get_role(role)
    return false if role.nil?
    role.allows_all?(options)
  end

  def allows_any_children?(options={})
    ident = options.delete(:identifier)
    return false if ident.nil? || !MiqProductFeature.feature_exists?(ident)

    child_idents = MiqProductFeature.feature_children(ident)
    self.allows_any?(options.merge(:identifiers => child_idents))
  end

  def self.allows_any_children?(role, options={})
    role = self.get_role(role)
    return false if role.nil?
    role.allows_any_children?(options)
  end

  def allows_all_children?(options={})
    ident = options.delete(:identifier)
    return false if ident.nil? || !MiqProductFeature.feature_exists?(ident)

    child_idents = MiqProductFeature.feature_children(ident)
    self.allows_all?(options.merge(:identifiers => child_idents))
  end

  def self.allows_all_children?(role, options={})
    role = self.get_role(role)
    return false if role.nil?
    role.allows_all_children?(options)
  end

  def self.get_role(role)
    case role
    when self, nil
      role
    when Integer
      includes(:miq_product_features).find_by_id(role)
    else
      includes(:miq_product_features).find_by_name(role)
    end
  end

  def self_service_role?
    [:user_or_group, :user].include?((self.settings || {}).fetch_path(:restrictions, :vms))
  end

  def limited_self_service_role?
    (self.settings || {}).fetch_path(:restrictions, :vms) == :user
  end

  def self.seed
    MiqRegion.my_region.lock do
      self.seed_from_array(YAML.load_file(FIXTURE_YAML))

      Dir.glob(File.join(FIXTURE_PATH, "*.yml")).each do |fixture|
        self.seed_from_array(YAML.load_file(fixture), true)
      end
    end
  end

  def self.seed_from_array(array, merge_features = false)
    array.each do |hash|
      feature_ids = hash.delete(:miq_product_feature_identifiers)

      hash[:miq_product_features] = MiqProductFeature.where(:identifier => feature_ids).to_a
      role = self.find_by_name(hash[:name]) || self.new(hash.except(:id))
      new_role = role.new_record?
      hash[:miq_product_features] &&= role.miq_product_features if !new_role && merge_features
      unless role.settings.nil? # Makse sure existing settings are merged in with the new ones.
        new_settings = hash.delete(:settings) || {}
        role.settings.merge!(new_settings)
      end
      role.update_attributes(hash.except(:id))
    end
  end

  def group_count
    self.miq_groups.count
  end

  def vm_restriction
{'fraction_non_alphanumeric': 0.11581050703521771, 'fraction_numerical': 0.0, 'mean_word_length': 3.5005620082427877, 'pattern_counts': {'":': 4, '<': 7, '<?xml version=': 0, '>': 36, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '488345', 'n_tokens_mistral': 4032, 'n_tokens_neox': 3901, 'n_words': 873}