Unhardcoded [security_compliance] section.

Change-Id: I075e4b7171e037e656f0ee330dbfb1cb15836d2c
Related-PROD: PROD-26638
Related-PROD: PROD-27663
diff --git a/README.rst b/README.rst
index 53d0198..7f7fbe7 100644
--- a/README.rst
+++ b/README.rst
@@ -895,6 +895,31 @@
           max_active_keys: 27
         ...
 
+Enable security compliance policies.
+-----------------------------------
+By default security compliance policies disabled. You are able to define follow params independency each other.
+
+Notice: To ignore `change_password_upon_first_use` requirement for specific users, such as service users,
+set the `options` attribute `ignore_change_password_upon_first_use`
+to `True` for the desired user via the update user API
+
+Notice: Symbol "$" should have escape character and looks like "$$".
+
+.. code-block:: yaml
+
+keystone:
+  server:
+    security_compliance:
+      disable_user_account_days_inactive: 90
+      lockout_failure_attempts: 5
+      lockout_duration: 600
+      password_expires_days: 90
+      unique_last_password_count: 10
+      minimum_password_age: 0
+      password_regex: '^(?=.*\d)(?=.*[a-zA-Z]).{7,}$$'
+      password_regex_description: 'Your password must contains at least 1 letter, 1 digit, and have a minimum length of 7 characters'
+      change_password_upon_first_use: true
+
 Upgrades
 ========
 
diff --git a/keystone/files/ocata/keystone.conf.Debian b/keystone/files/ocata/keystone.conf.Debian
index 3158842..3b73a85 100644
--- a/keystone/files/ocata/keystone.conf.Debian
+++ b/keystone/files/ocata/keystone.conf.Debian
@@ -2805,6 +2805,9 @@
 # (integer value)
 # Minimum value: 1
 #disable_user_account_days_inactive = <None>
+{%- if server.security_compliance.disable_user_account_days_inactive is defined and server.get('backend', 'sql') == 'sql' %}
+disable_user_account_days_inactive = {{ server.security_compliance.disable_user_account_days_inactive }}
+{%- endif %}
 
 # The maximum number of times that a user can fail to authenticate before the
 # user account is locked for the number of seconds specified by
@@ -2815,6 +2818,9 @@
 # backend for the `[identity] driver`. (integer value)
 # Minimum value: 1
 #lockout_failure_attempts = <None>
+{%- if server.security_compliance.lockout_failure_attempts is defined and server.get('backend', 'sql') == 'sql' %}
+lockout_failure_attempts = {{ server.security_compliance.lockout_failure_attempts }}
+{%- endif %}
 
 # The number of seconds a user account will be locked when the maximum number
 # of failed authentication attempts (as specified by `[security_compliance]
@@ -2824,6 +2830,9 @@
 # `[identity] driver`. (integer value)
 # Minimum value: 1
 #lockout_duration = 1800
+{%- if server.security_compliance.lockout_duration is defined and server.get('backend', 'sql') == 'sql' %}
+lockout_duration = {{ server.security_compliance.lockout_duration }}
+{%- endif %}
 
 # The number of days for which a password will be considered valid before
 # requiring it to be changed. This feature is disabled by default. If enabled,
@@ -2832,20 +2841,9 @@
 # `[identity] driver`. (integer value)
 # Minimum value: 1
 #password_expires_days = <None>
-
-# DEPRECATED: Comma separated list of user IDs to be ignored when checking if a
-# password is expired. Passwords for users in this list will not expire. This
-# feature will only be enabled if `[security_compliance] password_expires_days`
-# is set. (list value)
-# This option is deprecated for removal since O.
-# Its value may be silently ignored in the future.
-# Reason: Functionality added as a per-user option "ignore_password_expiry" in
-# Ocata. Each user that should ignore password expiry should have the value set
-# to "true" in the user's `options` attribute (e.g.
-# `user['options']['ignore_password_expiry'] = True`) with an "update_user"
-# call. This avoids the need to restart keystone to adjust the users that
-# ignore password expiry. This option will be removed in the Pike release.
-#password_expires_ignore_user_ids =
+{%- if server.security_compliance.password_expires_days is defined and server.get('backend', 'sql') == 'sql' %}
+password_expires_days = {{ server.security_compliance.password_expires_days }}
+{%- endif %}
 
 # This controls the number of previous user password iterations to keep in
 # history, in order to enforce that newly created passwords are unique. Setting
@@ -2854,6 +2852,9 @@
 # backend for the `[identity] driver`. (integer value)
 # Minimum value: 1
 #unique_last_password_count = 1
+{%- if server.security_compliance.unique_last_password_count is defined and server.get('backend', 'sql') == 'sql' %}
+unique_last_password_count = {{ server.security_compliance.unique_last_password_count }}
+{%- endif %}
 
 # The number of days that a password must be used before the user can change
 # it. This prevents users from changing their passwords immediately in order to
@@ -2865,6 +2866,9 @@
 # option should be less than the `password_expires_days`. (integer value)
 # Minimum value: 0
 #minimum_password_age = 0
+{%- if server.security_compliance.minimum_password_age is defined and server.get('backend', 'sql') == 'sql' %}
+minimum_password_age = {{ server.security_compliance.minimum_password_age }}
+{%- endif %}
 
 # The regular expression used to validate password strength requirements. By
 # default, the regular expression will match any password. The following is an
@@ -2872,12 +2876,18 @@
 # minimum length of 7 characters: ^(?=.*\d)(?=.*[a-zA-Z]).{7,}$ This feature
 # depends on the `sql` backend for the `[identity] driver`. (string value)
 #password_regex = <None>
+{%- if server.security_compliance.password_regex is defined and server.get('backend', 'sql') == 'sql' %}
+password_regex = {{ server.security_compliance.password_regex }}
+{%- endif %}
 
 # Describe your password regular expression here in language for humans. If a
 # password fails to match the regular expression, the contents of this
 # configuration variable will be returned to users to explain why their
 # requested password was insufficient. (string value)
 #password_regex_description = <None>
+{%- if server.security_compliance.password_regex_description is defined %}
+password_regex_description = {{ server.security_compliance.password_regex_description }}
+{%- endif %}
 
 # Enabling this option requires users to change their password when the user is
 # created, or upon administrative reset. Before accessing any services,
@@ -2888,7 +2898,9 @@
 # only applicable with the `sql` backend for the `[identity] driver`. (boolean
 # value)
 #change_password_upon_first_use = false
-
+{%- if server.security_compliance.change_password_upon_first_use is defined and server.get('backend', 'sql') == 'sql' %}
+change_password_upon_first_use = {{ server.security_compliance.change_password_upon_first_use }}
+{%- endif %}
 
 [shadow_users]
 
diff --git a/keystone/files/pike/keystone.conf.Debian b/keystone/files/pike/keystone.conf.Debian
index f2d2fbd..d484c23 100644
--- a/keystone/files/pike/keystone.conf.Debian
+++ b/keystone/files/pike/keystone.conf.Debian
@@ -2822,6 +2822,9 @@
 # (integer value)
 # Minimum value: 1
 #disable_user_account_days_inactive = <None>
+{%- if server.security_compliance.disable_user_account_days_inactive is defined and server.get('backend', 'sql') == 'sql' %}
+disable_user_account_days_inactive = {{ server.security_compliance.disable_user_account_days_inactive }}
+{%- endif %}
 
 # The maximum number of times that a user can fail to authenticate before the
 # user account is locked for the number of seconds specified by
@@ -2832,6 +2835,9 @@
 # backend for the `[identity] driver`. (integer value)
 # Minimum value: 1
 #lockout_failure_attempts = <None>
+{%- if server.security_compliance.lockout_failure_attempts is defined and server.get('backend', 'sql') == 'sql' %}
+lockout_failure_attempts = {{ server.security_compliance.lockout_failure_attempts }}
+{%- endif %}
 
 # The number of seconds a user account will be locked when the maximum number
 # of failed authentication attempts (as specified by `[security_compliance]
@@ -2841,6 +2847,9 @@
 # `[identity] driver`. (integer value)
 # Minimum value: 1
 #lockout_duration = 1800
+{%- if server.security_compliance.lockout_duration is defined and server.get('backend', 'sql') == 'sql' %}
+lockout_duration = {{ server.security_compliance.lockout_duration }}
+{%- endif %}
 
 # The number of days for which a password will be considered valid before
 # requiring it to be changed. This feature is disabled by default. If enabled,
@@ -2849,20 +2858,9 @@
 # `[identity] driver`. (integer value)
 # Minimum value: 1
 #password_expires_days = <None>
-
-# DEPRECATED: Comma separated list of user IDs to be ignored when checking if a
-# password is expired. Passwords for users in this list will not expire. This
-# feature will only be enabled if `[security_compliance] password_expires_days`
-# is set. (list value)
-# This option is deprecated for removal since O.
-# Its value may be silently ignored in the future.
-# Reason: Functionality added as a per-user option "ignore_password_expiry" in
-# Ocata. Each user that should ignore password expiry should have the value set
-# to "true" in the user's `options` attribute (e.g.
-# `user['options']['ignore_password_expiry'] = True`) with an "update_user"
-# call. This avoids the need to restart keystone to adjust the users that
-# ignore password expiry. This option will be removed in the Pike release.
-#password_expires_ignore_user_ids =
+{%- if server.security_compliance.password_expires_days is defined and server.get('backend', 'sql') == 'sql' %}
+password_expires_days = {{ server.security_compliance.password_expires_days }}
+{%- endif %}
 
 # This controls the number of previous user password iterations to keep in
 # history, in order to enforce that newly created passwords are unique. Setting
@@ -2871,6 +2869,9 @@
 # backend for the `[identity] driver`. (integer value)
 # Minimum value: 1
 #unique_last_password_count = 1
+{%- if server.security_compliance.unique_last_password_count is defined and server.get('backend', 'sql') == 'sql' %}
+unique_last_password_count = {{ server.security_compliance.unique_last_password_count }}
+{%- endif %}
 
 # The number of days that a password must be used before the user can change
 # it. This prevents users from changing their passwords immediately in order to
@@ -2882,6 +2883,9 @@
 # option should be less than the `password_expires_days`. (integer value)
 # Minimum value: 0
 #minimum_password_age = 0
+{%- if server.security_compliance.minimum_password_age is defined and server.get('backend', 'sql') == 'sql' %}
+minimum_password_age = {{ server.security_compliance.minimum_password_age }}
+{%- endif %}
 
 # The regular expression used to validate password strength requirements. By
 # default, the regular expression will match any password. The following is an
@@ -2889,12 +2893,18 @@
 # minimum length of 7 characters: ^(?=.*\d)(?=.*[a-zA-Z]).{7,}$ This feature
 # depends on the `sql` backend for the `[identity] driver`. (string value)
 #password_regex = <None>
+{%- if server.security_compliance.password_regex is defined and server.get('backend', 'sql') == 'sql' %}
+password_regex = {{ server.security_compliance.password_regex }}
+{%- endif %}
 
 # Describe your password regular expression here in language for humans. If a
 # password fails to match the regular expression, the contents of this
 # configuration variable will be returned to users to explain why their
 # requested password was insufficient. (string value)
 #password_regex_description = <None>
+{%- if server.security_compliance.password_regex_description is defined %}
+password_regex_description = {{ server.security_compliance.password_regex_description }}
+{%- endif %}
 
 # Enabling this option requires users to change their password when the user is
 # created, or upon administrative reset. Before accessing any services,
@@ -2905,7 +2915,9 @@
 # only applicable with the `sql` backend for the `[identity] driver`. (boolean
 # value)
 #change_password_upon_first_use = false
-
+{%- if server.security_compliance.change_password_upon_first_use is defined and server.get('backend', 'sql') == 'sql' %}
+change_password_upon_first_use = {{ server.security_compliance.change_password_upon_first_use }}
+{%- endif %}
 
 [shadow_users]
 
diff --git a/keystone/files/queens/keystone.conf.Debian b/keystone/files/queens/keystone.conf.Debian
index 492bb4f..f872c2f 100644
--- a/keystone/files/queens/keystone.conf.Debian
+++ b/keystone/files/queens/keystone.conf.Debian
@@ -1399,6 +1399,9 @@
 # user table. (integer value)
 # Minimum value: 1
 #disable_user_account_days_inactive = <None>
+{%- if server.security_compliance.disable_user_account_days_inactive is defined and server.get('backend', 'sql') == 'sql' %}
+disable_user_account_days_inactive = {{ server.security_compliance.disable_user_account_days_inactive }}
+{%- endif %}
 
 # The maximum number of times that a user can fail to authenticate
 # before the user account is locked for the number of seconds
@@ -1410,6 +1413,9 @@
 # `[identity] driver`. (integer value)
 # Minimum value: 1
 #lockout_failure_attempts = <None>
+{%- if server.security_compliance.lockout_failure_attempts is defined and server.get('backend', 'sql') == 'sql' %}
+lockout_failure_attempts = {{ server.security_compliance.lockout_failure_attempts }}
+{%- endif %}
 
 # The number of seconds a user account will be locked when the maximum
 # number of failed authentication attempts (as specified by
@@ -1420,6 +1426,9 @@
 # driver`. (integer value)
 # Minimum value: 1
 #lockout_duration = 1800
+{%- if server.security_compliance.lockout_duration is defined and server.get('backend', 'sql') == 'sql' %}
+lockout_duration = {{ server.security_compliance.lockout_duration }}
+{%- endif %}
 
 # The number of days for which a password will be considered valid
 # before requiring it to be changed. This feature is disabled by
@@ -1429,6 +1438,9 @@
 # value)
 # Minimum value: 1
 #password_expires_days = <None>
+{%- if server.security_compliance.password_expires_days is defined and server.get('backend', 'sql') == 'sql' %}
+password_expires_days = {{ server.security_compliance.password_expires_days }}
+{%- endif %}
 
 # This controls the number of previous user password iterations to
 # keep in history, in order to enforce that newly created passwords
@@ -1439,6 +1451,9 @@
 # for the `[identity] driver`. (integer value)
 # Minimum value: 1
 #unique_last_password_count = 1
+{%- if server.security_compliance.unique_last_password_count is defined and server.get('backend', 'sql') == 'sql' %}
+unique_last_password_count = {{ server.security_compliance.unique_last_password_count }}
+{%- endif %}
 
 # The number of days that a password must be used before the user can
 # change it. This prevents users from changing their passwords
@@ -1451,6 +1466,9 @@
 # be less than the `password_expires_days`. (integer value)
 # Minimum value: 0
 #minimum_password_age = 0
+{%- if server.security_compliance.minimum_password_age is defined and server.get('backend', 'sql') == 'sql' %}
+minimum_password_age = {{ server.security_compliance.minimum_password_age }}
+{%- endif %}
 
 # The regular expression used to validate password strength
 # requirements. By default, the regular expression will match any
@@ -1459,6 +1477,9 @@
 # ^(?=.*\d)(?=.*[a-zA-Z]).{7,}$ This feature depends on the `sql`
 # backend for the `[identity] driver`. (string value)
 #password_regex = <None>
+{%- if server.security_compliance.password_regex is defined and server.get('backend', 'sql') == 'sql' %}
+password_regex = {{ server.security_compliance.password_regex }}
+{%- endif %}
 
 # Describe your password regular expression here in language for
 # humans. If a password fails to match the regular expression, the
@@ -1466,6 +1487,9 @@
 # explain why their requested password was insufficient. (string
 # value)
 #password_regex_description = <None>
+{%- if server.security_compliance.password_regex_description is defined %}
+password_regex_description = {{ server.security_compliance.password_regex_description }}
+{%- endif %}
 
 # Enabling this option requires users to change their password when
 # the user is created, or upon administrative reset. Before accessing
@@ -1476,7 +1500,9 @@
 # is disabled by default. This feature is only applicable with the
 # `sql` backend for the `[identity] driver`. (boolean value)
 #change_password_upon_first_use = false
-
+{%- if server.security_compliance.change_password_upon_first_use is defined and server.get('backend', 'sql') == 'sql' %}
+change_password_upon_first_use = {{ server.security_compliance.change_password_upon_first_use }}
+{%- endif %}
 
 [shadow_users]
 
diff --git a/keystone/files/rocky/keystone.conf.Debian b/keystone/files/rocky/keystone.conf.Debian
index 0899d7a..b26138b 100644
--- a/keystone/files/rocky/keystone.conf.Debian
+++ b/keystone/files/rocky/keystone.conf.Debian
@@ -1317,6 +1317,9 @@
 # (integer value)
 # Minimum value: 1
 #disable_user_account_days_inactive = <None>
+{%- if server.security_compliance.disable_user_account_days_inactive is defined and server.get('backend', 'sql') == 'sql' %}
+disable_user_account_days_inactive = {{ server.security_compliance.disable_user_account_days_inactive }}
+{%- endif %}
 
 # The maximum number of times that a user can fail to authenticate before the
 # user account is locked for the number of seconds specified by
@@ -1327,6 +1330,9 @@
 # backend for the `[identity] driver`. (integer value)
 # Minimum value: 1
 #lockout_failure_attempts = <None>
+{%- if server.security_compliance.lockout_failure_attempts is defined and server.get('backend', 'sql') == 'sql' %}
+lockout_failure_attempts = {{ server.security_compliance.lockout_failure_attempts }}
+{%- endif %}
 
 # The number of seconds a user account will be locked when the maximum number
 # of failed authentication attempts (as specified by `[security_compliance]
@@ -1336,6 +1342,9 @@
 # `[identity] driver`. (integer value)
 # Minimum value: 1
 #lockout_duration = 1800
+{%- if server.security_compliance.lockout_duration is defined and server.get('backend', 'sql') == 'sql' %}
+lockout_duration = {{ server.security_compliance.lockout_duration }}
+{%- endif %}
 
 # The number of days for which a password will be considered valid before
 # requiring it to be changed. This feature is disabled by default. If enabled,
@@ -1344,6 +1353,9 @@
 # `[identity] driver`. (integer value)
 # Minimum value: 1
 #password_expires_days = <None>
+{%- if server.security_compliance.password_expires_days is defined and server.get('backend', 'sql') == 'sql' %}
+password_expires_days = {{ server.security_compliance.password_expires_days }}
+{%- endif %}
 
 # This controls the number of previous user password iterations to keep in
 # history, in order to enforce that newly created passwords are unique. The
@@ -1353,6 +1365,9 @@
 # depends on the `sql` backend for the `[identity] driver`. (integer value)
 # Minimum value: 1
 #unique_last_password_count = 1
+{%- if server.security_compliance.unique_last_password_count is defined and server.get('backend', 'sql') == 'sql' %}
+unique_last_password_count = {{ server.security_compliance.unique_last_password_count }}
+{%- endif %}
 
 # The number of days that a password must be used before the user can change
 # it. This prevents users from changing their passwords immediately in order to
@@ -1364,6 +1379,9 @@
 # option should be less than the `password_expires_days`. (integer value)
 # Minimum value: 0
 #minimum_password_age = 0
+{%- if server.security_compliance.minimum_password_age is defined and server.get('backend', 'sql') == 'sql' %}
+minimum_password_age = {{ server.security_compliance.minimum_password_age }}
+{%- endif %}
 
 # The regular expression used to validate password strength requirements. By
 # default, the regular expression will match any password. The following is an
@@ -1371,12 +1389,18 @@
 # minimum length of 7 characters: ^(?=.*\d)(?=.*[a-zA-Z]).{7,}$ This feature
 # depends on the `sql` backend for the `[identity] driver`. (string value)
 #password_regex = <None>
+{%- if server.security_compliance.password_regex is defined and server.get('backend', 'sql') == 'sql' %}
+password_regex = {{ server.security_compliance.password_regex }}
+{%- endif %}
 
 # Describe your password regular expression here in language for humans. If a
 # password fails to match the regular expression, the contents of this
 # configuration variable will be returned to users to explain why their
 # requested password was insufficient. (string value)
 #password_regex_description = <None>
+{%- if server.security_compliance.password_regex_description is defined %}
+password_regex_description = {{ server.security_compliance.password_regex_description }}
+{%- endif %}
 
 # Enabling this option requires users to change their password when the user is
 # created, or upon administrative reset. Before accessing any services,
@@ -1387,7 +1411,9 @@
 # only applicable with the `sql` backend for the `[identity] driver`. (boolean
 # value)
 #change_password_upon_first_use = false
-
+{%- if server.security_compliance.change_password_upon_first_use is defined and server.get('backend', 'sql') == 'sql' %}
+change_password_upon_first_use = {{ server.security_compliance.change_password_upon_first_use }}
+{%- endif %}
 
 [shadow_users]
 
diff --git a/keystone/map.jinja b/keystone/map.jinja
index d97e1a5..cbf1f03 100644
--- a/keystone/map.jinja
+++ b/keystone/map.jinja
@@ -31,6 +31,7 @@
         'notification': False,
         'roles': ['admin', 'Member'],
         'cacert': '/etc/ssl/certs/ca-certificates.crt',
+        'security_compliance': {},
         'logging': {
           'app_name': 'keystone',
           'log_appender': false,
@@ -56,6 +57,7 @@
         'notification': False,
         'roles': ['admin', 'Member'],
         'cacert': '/etc/pki/tls/certs/ca-bundle.crt',
+        'security_compliance': {},
         'logging': {
           'app_name': 'keystone',
           'log_appender': false,
diff --git a/tests/integration/ocata/single/config_spec.rb b/tests/integration/ocata/single/config_spec.rb
index 3975487..acead69 100644
--- a/tests/integration/ocata/single/config_spec.rb
+++ b/tests/integration/ocata/single/config_spec.rb
@@ -118,6 +118,18 @@
   'remote_id_attribute' => 'HTTP_OIDC_ISS'
 }
 
+keystone_security_compliance = {
+  'disable_user_account_days_inactive' => '90',
+  'lockout_failure_attempts'           => '5',
+  'lockout_duration'                   => '600',
+  'password_expires_days'              => '90',
+  'unique_last_password_count'         => '10',
+  'minimum_password_age'               => '0',
+  'password_regex'                     => '^(?=.*\d)(?=.*[a-zA-Z]).{7,}$$',
+  'password_regex_description'         => 'Your password must contains at least 1 letter, 1 digit, and have a minimum length of 7 characters',
+  'change_password_upon_first_use'     => 'True',
+}
+
 keystone_federation = {
   'cache_group_membership_in_db' => 'True',
 }
@@ -215,6 +227,9 @@
       its('saml2') {
         should include(keystone_saml2)
       }
+      its('security_compliance') {
+        should include(keystone_security_compliance)
+      }
       its('federation') {
         should include(keystone_federation)
       }
diff --git a/tests/integration/pike/single/config_spec.rb b/tests/integration/pike/single/config_spec.rb
index 5937921..7f7bd01 100644
--- a/tests/integration/pike/single/config_spec.rb
+++ b/tests/integration/pike/single/config_spec.rb
@@ -118,6 +118,18 @@
   'remote_id_attribute' => 'HTTP_OIDC_ISS'
 }
 
+keystone_security_compliance = {
+  'disable_user_account_days_inactive' => '90',
+  'lockout_failure_attempts'           => '5',
+  'lockout_duration'                   => '600',
+  'password_expires_days'              => '90',
+  'unique_last_password_count'         => '10',
+  'minimum_password_age'               => '0',
+  'password_regex'                     => '^(?=.*\d)(?=.*[a-zA-Z]).{7,}$$',
+  'password_regex_description'         => 'Your password must contains at least 1 letter, 1 digit, and have a minimum length of 7 characters',
+  'change_password_upon_first_use'     => 'True',
+}
+
 keystone_federation = {
   'cache_group_membership_in_db' => 'True',
 }
@@ -215,6 +227,9 @@
       its('saml2') {
         should include(keystone_saml2)
       }
+      its('security_compliance') {
+        should include(keystone_security_compliance)
+      }
       its('federation') {
         should include(keystone_federation)
       }
diff --git a/tests/integration/queens/single/config_spec.rb b/tests/integration/queens/single/config_spec.rb
index d47ef22..cda5269 100644
--- a/tests/integration/queens/single/config_spec.rb
+++ b/tests/integration/queens/single/config_spec.rb
@@ -170,6 +170,18 @@
   'remote_id_attribute' => 'HTTP_OIDC_ISS'
 }
 
+keystone_security_compliance = {
+  'disable_user_account_days_inactive' => '90',
+  'lockout_failure_attempts'           => '5',
+  'lockout_duration'                   => '600',
+  'password_expires_days'              => '90',
+  'unique_last_password_count'         => '10',
+  'minimum_password_age'               => '0',
+  'password_regex'                     => '^(?=.*\d)(?=.*[a-zA-Z]).{7,}$$',
+  'password_regex_description'         => 'Your password must contains at least 1 letter, 1 digit, and have a minimum length of 7 characters',
+  'change_password_upon_first_use'     => 'True',
+}
+
 keystone_federation = {
   'cache_group_membership_in_db' => 'True',
 }
@@ -267,6 +279,9 @@
       its('saml2') {
         should include(keystone_saml2)
       }
+      its('security_compliance') {
+        should include(keystone_security_compliance)
+      }
       its('federation') {
         should include(keystone_federation)
       }
diff --git a/tests/integration/rocky/single/config_spec.rb b/tests/integration/rocky/single/config_spec.rb
index bbff67d..e07be3e 100644
--- a/tests/integration/rocky/single/config_spec.rb
+++ b/tests/integration/rocky/single/config_spec.rb
@@ -142,6 +142,18 @@
   'remote_id_attribute' => 'HTTP_OIDC_ISS'
 }
 
+keystone_security_compliance = {
+  'disable_user_account_days_inactive' => '90',
+  'lockout_failure_attempts'           => '5',
+  'lockout_duration'                   => '600',
+  'password_expires_days'              => '90',
+  'unique_last_password_count'         => '10',
+  'minimum_password_age'               => '0',
+  'password_regex'                     => '^(?=.*\d)(?=.*[a-zA-Z]).{7,}$$',
+  'password_regex_description'         => 'Your password must contains at least 1 letter, 1 digit, and have a minimum length of 7 characters',
+  'change_password_upon_first_use'     => 'True',
+}
+
 keystone_federation = {
   'cache_group_membership_in_db' => 'True',
 }
@@ -239,6 +251,9 @@
       its('saml2') {
         should include(keystone_saml2)
       }
+      its('security_compliance') {
+        should include(keystone_security_compliance)
+      }
       its('federation') {
         should include(keystone_federation)
       }
diff --git a/tests/pillar/cluster.sls b/tests/pillar/cluster.sls
index d00b666..24b17f4 100644
--- a/tests/pillar/cluster.sls
+++ b/tests/pillar/cluster.sls
@@ -28,6 +28,16 @@
       location: /etc/keystone/fernet-keys/
     notification: false
     notification_format: cadf
+    security_compliance:
+      disable_user_account_days_inactive: 90
+      lockout_failure_attempts: 5
+      lockout_duration: 600
+      password_expires_days: 90
+      unique_last_password_count: 10
+      minimum_password_age: 0
+      password_regex: '^(?=.*\d)(?=.*[a-zA-Z]).{7,}$$'
+      password_regex_description: 'Your password must contains at least 1 letter, 1 digit, and have a minimum length of 7 characters'
+      change_password_upon_first_use: True
     logging:
       log_appender: false
       log_handlers:
diff --git a/tests/pillar/single.sls b/tests/pillar/single.sls
index 052657f..f870998 100644
--- a/tests/pillar/single.sls
+++ b/tests/pillar/single.sls
@@ -73,6 +73,16 @@
       allow_expired_window: 86400
     notification: true
     notification_format: cadf
+    security_compliance:
+      disable_user_account_days_inactive: 90
+      lockout_failure_attempts: 5
+      lockout_duration: 600
+      password_expires_days: 90
+      unique_last_password_count: 10
+      minimum_password_age: 0
+      password_regex: '^(?=.*\d)(?=.*[a-zA-Z]).{7,}$$'
+      password_regex_description: 'Your password must contains at least 1 letter, 1 digit, and have a minimum length of 7 characters'
+      change_password_upon_first_use: True
     logging:
       debug: true
       log_file: 'logfile.log'