diff --git a/app/controllers/ops_controller/ops_rbac.rb b/app/controllers/ops_controller/ops_rbac.rb index 21137282e5b..fb1a370aaff 100644 --- a/app/controllers/ops_controller/ops_rbac.rb +++ b/app/controllers/ops_controller/ops_rbac.rb @@ -17,6 +17,8 @@ def role_allows?(**options) end options[:feature] = MiqProductFeature.tenant_identifier(options[:feature], id) + # dynamic tenant feature identifiers need to bypass feature validation + options[:skip_feature_validation] = true end super(**options) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f2961b5c1cd..71fbf779994 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -104,6 +104,16 @@ def role_allows?(**options) return false end + # ops_rbac role_allows's dynamic tenant features are supported in rbac but not + # with direct lookup in validate_features so we skip it. + validate_features(features) unless !!options.delete(:skip_feature_validation) + + Rbac.role_allows?(:user => User.current_user, **options) rescue false + end + module_function :role_allows? + public :role_allows? + + def validate_features(features) # Detect if queried features are missing from the database and possibly invalid if !Rails.env.production? && features.detect { |feature| !MiqProductFeature.feature_exists?(feature) } message = "#{__method__} no feature was found with identifier: #{features.inspect}. Correct the identifier or add it to miq_product_features.yml." @@ -114,12 +124,8 @@ def role_allows?(**options) raise("#{message} Note: detected features: #{identifiers.inspect}") end end - - Rbac.role_allows?(:user => User.current_user, **options) rescue false end - - module_function :role_allows? - public :role_allows? + module_function :validate_features # NB: This differs from controller_for_model; until they're unified, # make sure you have the right one. diff --git a/cypress/e2e/ui/Settings/Application-Settings/settings_access_control.cy.js b/cypress/e2e/ui/Settings/Application-Settings/settings_access_control.cy.js new file mode 100644 index 00000000000..33ea04a4458 --- /dev/null +++ b/cypress/e2e/ui/Settings/Application-Settings/settings_access_control.cy.js @@ -0,0 +1,51 @@ +/* eslint-disable no-undef */ +import { flashClassMap } from '../../../../support/assertions/assertion_constants'; + +describe('Settings > Application Settings > Access Control', () => { + // Navigation + const PRIMARY_MENU_OPTION = 'Settings'; + const SECONDARY_MENU_OPTION = 'Application Settings'; + const ACCORDION = 'Access Control'; + const TOOLBAR_MENU = 'Configuration'; + + // Created item information + const INITIAL_TENANT_NAME = 'Test-name'; + const INITIAL_TENANT_DESCRIPTION = 'test description'; + + // CRUD actions + const FLASH_MESSAGE_OPERATION_ADDED = 'added'; + const FLASH_MESSAGE_OPERATION_DELETED = 'delete'; + const DELETE_ITEM = 'Delete this item'; + + beforeEach(() => { + cy.login(); + cy.menu(PRIMARY_MENU_OPTION, SECONDARY_MENU_OPTION); + cy.accordion(ACCORDION); + }); + + it('should be able to create and delete a tenant', () => { + cy.selectAccordionItem([ + /^ManageIQ Region/, + 'Tenants', + 'My Company', + ]); + + cy.toolbar(TOOLBAR_MENU, 'Add child Tenant to this Tenant'); + cy.getFormInputFieldById('name').type(INITIAL_TENANT_NAME); + cy.getFormInputFieldById('description').type(INITIAL_TENANT_DESCRIPTION); + cy.getFormFooterButtonByType('Add', 'submit').click(); + cy.expect_flash(flashClassMap.success, FLASH_MESSAGE_OPERATION_ADDED); + cy.selectAccordionItem([ + /^ManageIQ Region/, + 'Tenants', + 'My Company', + INITIAL_TENANT_NAME + ]); + + cy.expect_browser_confirm_with_text({ + confirmTriggerFn: () => cy.toolbar(TOOLBAR_MENU, DELETE_ITEM), + containsText: DELETE_ITEM, + }); + cy.expect_flash(flashClassMap.success, FLASH_MESSAGE_OPERATION_DELETED); + }); +});