diff --git a/config.yaml b/config.yaml index 30a30864..b7fc9cd2 100644 --- a/config.yaml +++ b/config.yaml @@ -512,6 +512,7 @@ options: this option sets True as the default value, which is consistent with the default value 'WSGISocketRotation On' in Apache. This option should be used with caution. Please read the Apache doc page for more information. + extra-regions: type: string default: "{}" @@ -528,3 +529,11 @@ options: "another cluster": "https://another.example.com/identity/v3" } + mfa-totp-enabled: + type: boolean + default: False + description: | + Allow users to enable TOTP Authentication form. If not configured, this option sets False + as the default value, which in turns does not display the form for MFA enabled users. If + this option is set to True, Horizon will display a second login form requesting the TOTP + code for MFA enabled users. diff --git a/hooks/horizon_contexts.py b/hooks/horizon_contexts.py index 79fd720c..2676b021 100644 --- a/hooks/horizon_contexts.py +++ b/hooks/horizon_contexts.py @@ -304,6 +304,8 @@ class HorizonContext(OSContextGenerator): config('enable-router-panel'), 'retrieve_network_data_when_listing_instances': config('retrieve-network-data-when-listing-instances'), + 'openstack_keystone_mfa_totp_enabled': + config('mfa-totp-enabled'), } return ctxt diff --git a/templates/bobcat/local_settings.py b/templates/bobcat/local_settings.py index 2b7c1c3d..5e489e53 100644 --- a/templates/bobcat/local_settings.py +++ b/templates/bobcat/local_settings.py @@ -1053,3 +1053,13 @@ SITE_BRANDING_LINK = "{{ site_branding_link }}" {%- if help_url %} HORIZON_CONFIG["help_url"] = "{{ help_url }}" {%- endif %} + +{%- if openstack_keystone_mfa_totp_enabled %} +OPENSTACK_KEYSTONE_MFA_TOTP_ENABLED = "{{ openstack_keystone_mfa_totp_enabled }}" + +AUTHENTICATION_PLUGINS = [ + 'openstack_auth.plugin.totp.TotpPlugin', + 'openstack_auth.plugin.password.PasswordPlugin', + 'openstack_auth.plugin.token.TokenPlugin' +] +{%- endif %} diff --git a/unit_tests/test_horizon_contexts.py b/unit_tests/test_horizon_contexts.py index 7e960e40..7afcdb8e 100644 --- a/unit_tests/test_horizon_contexts.py +++ b/unit_tests/test_horizon_contexts.py @@ -147,6 +147,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -189,6 +190,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -232,6 +234,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -277,6 +280,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": True, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -321,6 +325,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": False, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -364,6 +369,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -406,6 +412,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -448,6 +455,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -491,6 +499,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -537,6 +546,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -579,6 +589,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -629,6 +640,7 @@ class TestHorizonContexts(CharmTestCase): "enable_router_panel": True, "retrieve_network_data_when_listing_instances": ( False), + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -670,6 +682,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_key": None, "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, + "openstack_keystone_mfa_totp_enabled": False, "retrieve_network_data_when_listing_instances": True, } ) @@ -713,6 +726,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -756,6 +770,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -799,6 +814,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -842,6 +858,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -885,6 +902,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -928,6 +946,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -975,6 +994,7 @@ class TestHorizonContexts(CharmTestCase): "create_instance_flavor_sort_reverse": False, "enable_router_panel": True, "retrieve_network_data_when_listing_instances": True, + "openstack_keystone_mfa_totp_enabled": False, } ) @@ -984,6 +1004,13 @@ class TestHorizonContexts(CharmTestCase): self.assertTrue(horizon_contexts .HorizonContext()()['disable_instance_snapshot']) + def test_HorizonContext_can_set_openstack_keystone_mfa_totp_enabled(self): + self.maxDiff = 900 + self.test_config.set('mfa-totp-enabled', True) + self.assertTrue(horizon_contexts + .HorizonContext()() + ['openstack_keystone_mfa_totp_enabled']) + def test_IdentityServiceContext_not_related(self): self.relation_ids.return_value = [] self.context_complete.return_value = False