diff --git a/app/models/miq_group.rb b/app/models/miq_group.rb index 71888cbc4114..ebd51c2dc4fe 100644 --- a/app/models/miq_group.rb +++ b/app/models/miq_group.rb @@ -60,8 +60,11 @@ def settings=(new_settings) super(indifferent_settings) end - def self.with_roles_excluding(disallowed_roles) - includes(:miq_user_role).where.not(:miq_user_roles => {:name => disallowed_roles}) + def self.with_roles_excluding(identifier) + where.not(:id => MiqGroup.joins(:miq_product_features) + .where(:miq_product_features => {:identifier => identifier}) + .select(:id) + ) end def self.next_sequence diff --git a/app/models/miq_product_feature.rb b/app/models/miq_product_feature.rb index 6f49b7ef803a..c1cb16380c83 100644 --- a/app/models/miq_product_feature.rb +++ b/app/models/miq_product_feature.rb @@ -1,4 +1,6 @@ class MiqProductFeature < ApplicationRecord + SUPER_ADMIN_FEATURE = "everything" + ADMIN_FEATURE = "admin" acts_as_tree has_and_belongs_to_many :miq_user_roles, :join_table => :miq_roles_features @@ -107,7 +109,7 @@ def self.seed_features(path = FIXTURE_PATH) features = all.to_a.index_by(&:identifier) seen = seed_from_hash(YAML.load_file(fixture_yaml), seen, nil, features) - root_feature = MiqProductFeature.find_by(:identifier => 'everything') + root_feature = MiqProductFeature.find_by(:identifier => SUPER_ADMIN_FEATURE) Dir.glob(path.join("*.yml")).each do |fixture| seed_from_hash(YAML.load_file(fixture), seen, root_feature) end diff --git a/app/models/miq_user_role.rb b/app/models/miq_user_role.rb index 3698a7274627..cbd73294bdb5 100644 --- a/app/models/miq_user_role.rb +++ b/app/models/miq_user_role.rb @@ -1,6 +1,4 @@ class MiqUserRole < ApplicationRecord - SUPER_ADMIN_ROLE_NAME = "EvmRole-super_administrator" - ADMIN_ROLE_NAME = "EvmRole-administrator" DEFAULT_TENANT_ROLE_NAME = "EvmRole-tenant_administrator" has_many :entitlements, :dependent => :restrict_with_exception @@ -64,8 +62,11 @@ def limited_self_service? (settings || {}).fetch_path(:restrictions, :vms) == :user end - def self.with_roles_excluding(disallowed_roles) - where.not(:name => disallowed_roles) + def self.with_roles_excluding(identifier) + where.not(:id => MiqUserRole.joins(:miq_product_features) + .where(:miq_product_features => {:identifier => identifier}) + .select(:id) + ) end def self.seed @@ -103,11 +104,11 @@ def vm_restriction end def super_admin_user? - name == SUPER_ADMIN_ROLE_NAME + allows?(:identifier => MiqProductFeature::SUPER_ADMIN_FEATURE) end def admin_user? - name == SUPER_ADMIN_ROLE_NAME || name == ADMIN_ROLE_NAME + allows_any?(:identifiers => [MiqProductFeature::SUPER_ADMIN_FEATURE, MiqProductFeature::ADMIN_FEATURE]) end def self.default_tenant_role diff --git a/app/models/user.rb b/app/models/user.rb index e5853fee025c..c3fe2f642116 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -25,7 +25,8 @@ class User < ApplicationRecord belongs_to :current_group, :class_name => "MiqGroup" has_and_belongs_to_many :miq_groups scope :superadmins, lambda { - joins(:miq_groups => :miq_user_role).where(:miq_user_roles => {:name => MiqUserRole::SUPER_ADMIN_ROLE_NAME }) + joins(:miq_groups => {:miq_user_role => :miq_product_features}) + .where(:miq_product_features => {:identifier => MiqProductFeature::SUPER_ADMIN_FEATURE }) } virtual_has_many :active_vms, :class_name => "VmOrTemplate" @@ -50,8 +51,11 @@ class User < ApplicationRecord scope :with_same_userid, ->(id) { where(:userid => User.find(id).userid) } - def self.with_roles_excluding(disallowed_roles) - includes(:miq_groups => :miq_user_role).where.not(:miq_user_roles => {:name => disallowed_roles}) + def self.with_roles_excluding(identifier) + where.not(:id => User.joins(:miq_groups => :miq_product_features) + .where(:miq_product_features => {:identifier => identifier}) + .select(:id) + ) end def self.scope_by_tenant? diff --git a/db/fixtures/miq_product_features.yml b/db/fixtures/miq_product_features.yml index 244a00b45d72..49d7788bf92e 100644 --- a/db/fixtures/miq_product_features.yml +++ b/db/fixtures/miq_product_features.yml @@ -5,6 +5,12 @@ :identifier: everything :children: +# Special Admin Functionality +- :name: Admin + :description: Special Admin Functionality + :feature_type: admin + :identifier: admin + # Workloads Explorer - :name: Workloads :description: Workloads Views diff --git a/db/fixtures/miq_user_roles.yml b/db/fixtures/miq_user_roles.yml index 23c2bd8c71e5..17746a1848ec 100644 --- a/db/fixtures/miq_user_roles.yml +++ b/db/fixtures/miq_user_roles.yml @@ -9,6 +9,7 @@ :read_only: true :miq_product_feature_identifiers: - about + - admin - all_vm_rules - automation_manager - embedded_automation_manager diff --git a/lib/rbac/filterer.rb b/lib/rbac/filterer.rb index 50e09cc9e09c..b9df1e477eaa 100644 --- a/lib/rbac/filterer.rb +++ b/lib/rbac/filterer.rb @@ -514,17 +514,19 @@ def scope_for_user_role_group(klass, scope, miq_group, user, managed_filters) if user_or_group.try!(:self_service?) && MiqUserRole != klass scope.where(:id => klass == User ? user.id : miq_group.id) else - if user_or_group.miq_user_role_name == 'EvmRole-tenant_administrator' - scope = scope.with_roles_excluding(%w(EvmRole-super_administrator EvmRole-administrator)) + # hide creating admin group / roles from tenant administrators + if !user_or_group.miq_user_role&.admin_user? + scope = scope.with_roles_excluding([MiqProductFeature::SUPER_ADMIN_FEATURE, MiqProductFeature::ADMIN_FEATURE]) end - if MiqUserRole != klass + if MiqUserRole == klass + scope + else filtered_ids = pluck_ids(get_managed_filter_object_ids(scope, managed_filters)) # Non admins can only see their own groups scope = scope.with_groups(user.miq_group_ids) if !user_or_group.miq_user_role&.admin_user? + scope_by_ids(scope, filtered_ids) end - - scope_by_ids(scope, filtered_ids) end end diff --git a/spec/factories/miq_user_role.rb b/spec/factories/miq_user_role.rb index e760a59913af..e089e410d5aa 100644 --- a/spec/factories/miq_user_role.rb +++ b/spec/factories/miq_user_role.rb @@ -12,6 +12,7 @@ name { |ur| ur.role ? "EvmRole-#{ur.role}" : generate(:miq_user_role_name) } after(:build) do |user, evaluator| + e_features = evaluator.features if evaluator.role.present? @system_roles ||= YAML.load_file(MiqUserRole::FIXTURE_YAML) seeded_role = @system_roles.detect { |role| role[:name] == "EvmRole-#{evaluator.role}" } @@ -20,10 +21,18 @@ user.read_only = seeded_role[:read_only] user.settings = seeded_role[:settings] end + if e_features.blank? + # admins now using a feature instead of a roll + if evaluator.role == "super_administrator" + e_features = MiqProductFeature::SUPER_ADMIN_FEATURE + elsif evaluator.role == "administrator" + e_features = MiqProductFeature::ADMIN_FEATURE + end + end end - if evaluator.features.present? - user.miq_product_features = Array.wrap(evaluator.features).map do |f| + if e_features.present? + user.miq_product_features = Array.wrap(e_features).map do |f| if f.kind_of?(MiqProductFeature) # TODO: remove class reference f else