Merge "Fix Python version for Travis CI tests"
diff --git a/README.rst b/README.rst
index afd5dce..6d6a085 100644
--- a/README.rst
+++ b/README.rst
@@ -105,7 +105,9 @@
         secure: True
 
 
-Horizon package setup with SSL
+Horizon package setup with SSL.
+
+Important: for the sake of backwards compatibility `ssl_no_verify` attribute defaults to True when horizon:server:identity:encryption is set to 'ssl'.
 
 .. code-block:: yaml
 
@@ -114,6 +116,7 @@
         enabled: true
         secret_key: MEGASECRET
         version: juno
+        ssl_no_verify: false
         ssl:
           enabled: true
           authority: CA_Authority
@@ -387,6 +390,46 @@
             mail:
               host: '127.0.0.1'
 
+Set advanced theme options (for Horizon version Mitaka and newer).
+
+Full example:
+
+.. code-block:: yaml
+
+  horizon:
+    server:
+      themes:
+        default: default                           # optional, default: "default"
+        directory: themes                          # optional, default: "themes"
+        cookie_name: theme                         # optional, default: "theme"
+        available:
+          default:                                 # slug
+            name: "Default"                        # display name
+            description: "Default style theme"
+            path: "themes/default"                 # optional, default: "<directory>/<slug>", e.g. "themes/default"
+            enabled: True
+          material:
+            name: "Material"
+            description: "Google's Material Design style theme"
+            path: "themes/material"
+            enabled: True
+
+Minimal example:
+
+.. code-block:: yaml
+
+  horizon:
+    server:
+      theme:
+        available:
+          default:                                 # slug
+            name: "Default"                        # display name
+            description: "Default style theme"
+          material:
+            name: "Material"
+            description: "Google's Material Design style theme"
+
+
 API versions override
 
 .. code-block:: yaml
@@ -429,7 +472,9 @@
               address: https://github.com/openstack/horizon.git
               rev: stable/juno
 
-Enable WebSSO feature
+Enable WebSSO feature. Define a list of choices [supported choices: oidc, saml2], `credentials` choice will be automatically appended and choice description is predefined. DEPRECATED
+
+WebSSO with credentials and saml2
 
 .. code-block:: yaml
 
@@ -439,10 +484,58 @@
         websso:
           login_url: "WEBROOT + 'auth/login/'"
           logout_url: "WEBROOT + 'auth/logout/'"
+          login_redirect_url: "WEBROOT + 'project/'"
           websso_choices:
             - saml2
-            - oidc
 
+Enable WebSSO feature. Define a map of choices in the following format: `{"<choice_name>": {"description": "<choice_description>"}`.
+
+WebSSO with saml2 and credentials
+
+.. code-block:: yaml
+
+    horizon:
+      server:
+        enabled: true
+        websso:
+          login_url: "WEBROOT + 'auth/login/'"
+          logout_url: "WEBROOT + 'auth/logout/'"
+          login_redirect_url: "WEBROOT + 'project/'"
+          websso_choices:
+            saml2:
+              description: "Security Assertion Markup Language"
+            credentials:
+              description: "Keystone Credentials"
+
+WebSSO with IDP mapping.
+
+.. code-block:: yaml
+
+    horizon:
+      server:
+        enabled: true
+        websso:
+          login_url: "WEBROOT + 'auth/login/'"
+          logout_url: "WEBROOT + 'auth/logout/'"
+          login_redirect_url: "WEBROOT + 'project/'"
+          websso_choices:
+            credentials:
+              description: "Keystone Credentials"
+            saml2:
+              description: "Security Assertion Markup Language"
+            oidc:
+              description: "OpenID Connect"
+            myidp_oidc:
+              description: "Acme Corporation - OpenID Connect"
+            myidp_saml2:
+              description: "Acme Corporation - SAML2"
+          idp_mapping:
+            myidp_oidc:
+              id: myidp
+              protocol: oidc
+            myidp_saml2:
+              id: myidp
+              protocol: saml2
 
 More Information
 ================
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..704143c
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,36 @@
+salt-formula-horizon (2016.12.1-1xenial1) xenial; urgency=medium
+
+  * Build for Ubuntu xenial
+
+ -- Filip Pytloun <filip@pytloun.cz>  Tue, 24 Jan 2017 12:21:42 +0100
+
+salt-formula-horizon (2016.12.1-1) unstable; urgency=medium
+
+  * New upstream release
+  * d/{control,copyright}: Use my @debian.org email address
+  * Bumped debhelper version to 10
+
+ -- Ondřej Nový <onovy@debian.org>  Sun, 25 Dec 2016 17:05:52 +0100
+
+salt-formula-horizon (2016.4.2-2) unstable; urgency=medium
+
+  * Added Debian tests
+
+ -- Ondřej Nový <novy@ondrej.org>  Wed, 08 Jun 2016 21:27:18 +0200
+
+salt-formula-horizon (2016.4.2-1) unstable; urgency=medium
+
+  [ Ondřej Nový ]
+  * d/copyright: Added myself to Debian part
+  * Added myself as uploader
+
+  [ Filip Pytloun ]
+  * New upstream release
+
+ -- Ondřej Nový <novy@ondrej.org>  Sat, 23 Apr 2016 19:10:28 +0200
+
+salt-formula-horizon (2016.4.1-1) unstable; urgency=medium
+
+  * Initial release (Closes: #821918)
+
+ -- Filip Pytloun <filip@pytloun.cz>  Wed, 20 Apr 2016 16:04:32 +0200
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..305b378
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,24 @@
+Source: salt-formula-horizon
+Maintainer: PKG OpenStack <openstack-devel@lists.alioth.debian.org>
+Uploaders: Filip Pytloun <filip@pytloun.cz>,
+           Ondřej Nový <onovy@debian.org>,
+Section: admin
+Priority: extra
+Build-Depends: debhelper (>= 9),
+               openstack-pkg-tools,
+Build-Depends-Indep: python-all,
+                     python-yaml,
+Standards-Version: 3.9.6
+Homepage: https://wiki.openstack.org/wiki/OpenStackSalt
+Vcs-Browser: https://anonscm.debian.org/cgit/openstack/salt-formula-horizon.git/
+Vcs-Git: https://anonscm.debian.org/git/openstack/salt-formula-horizon.git
+
+Package: salt-formula-horizon
+Architecture: all
+Depends: ${misc:Depends},
+Description: Salt formula for OpenStack Horizon
+ Salt is a powerful remote execution manager that can be used to
+ administer servers in a fast and efficient way.
+ .
+ This SaltStack formula manages both installation and configuration of
+ OpenStack Horizon.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..10bca0d
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,28 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: salt-formula-horizon
+Source: https://github.com/openstack/salt-formula-horizon
+
+Files: *
+Copyright: 2014-2016 tcp cloud
+License: Apache-2.0
+
+Files: debian/*
+Copyright: (c) 2016, Filip Pytloun <filip@pytloun.cz>
+           (c) 2016, Ondřej Nový <onovy@debian.org>
+License: Apache-2.0
+
+License: Apache-2.0
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ .
+    http://www.apache.org/licenses/LICENSE-2.0
+ .
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ .
+ On Debian-based systems the full text of the Apache version 2.0 license
+ can be found in `/usr/share/common-licenses/Apache-2.0'.
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..a1320b1
--- /dev/null
+++ b/debian/docs
@@ -0,0 +1 @@
+README.rst
diff --git a/debian/gbp.conf b/debian/gbp.conf
new file mode 100644
index 0000000..d6f9d4f
--- /dev/null
+++ b/debian/gbp.conf
@@ -0,0 +1,8 @@
+[DEFAULT]
+upstream-branch = master
+debian-branch = debian/xenial
+upstream-tag = %(version)s
+compression = xz
+
+[buildpackage]
+export-dir = ../build-area/
diff --git a/debian/lintian-overrides b/debian/lintian-overrides
new file mode 100644
index 0000000..f8ea75e
--- /dev/null
+++ b/debian/lintian-overrides
@@ -0,0 +1,2 @@
+salt-formula-horizon binary: shell-script-fails-syntax-check usr/share/salt-formulas/env/horizon/files/gunicorn_start
+salt-formula-horizon binary: script-not-executable usr/share/salt-formulas/env/horizon/files/gunicorn_start
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..f7d5908
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,7 @@
+#!/usr/bin/make -f
+
+include /usr/share/openstack-pkg-tools/pkgos.make
+
+%:
+	dh $@
+
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/debian/tests/control b/debian/tests/control
new file mode 100644
index 0000000..685e62b
--- /dev/null
+++ b/debian/tests/control
@@ -0,0 +1,2 @@
+Test-Command: cd tests && ./run_tests.sh
+Restrictions: allow-stderr
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..7a16569
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,3 @@
+version=3
+opts="uversionmangle=s/\.(b|rc)/~$1/" \
+https://github.com/openstack/salt-formula-horizon/tags .*/(\d[\d\.]+)\.tar\.gz
diff --git a/horizon/files/horizon_settings/_keystone_settings.py b/horizon/files/horizon_settings/_keystone_settings.py
index 299cef8..c31f721 100644
--- a/horizon/files/horizon_settings/_keystone_settings.py
+++ b/horizon/files/horizon_settings/_keystone_settings.py
@@ -55,8 +55,15 @@
 OPENSTACK_KEYSTONE_DEFAULT_ROLE = "Member"
 
 # Disable SSL certificate checks (useful for self-signed certificates):
+{#- NO_VERIFY is set to True if identity.encryption == 'ssl', unless explicitly set in the pillar for the sake of backwards compatibility #}
 {%- if app.identity.encryption == 'ssl' %}
-OPENSTACK_SSL_NO_VERIFY = True
+{%- set _no_verify = True %}
+{%- endif %}
+{%- if app.ssl_no_verify is defined %}
+{%- set _no_verify = app.ssl_no_verify %}
+{%- endif %}
+{%- if _no_verify is defined %}
+OPENSTACK_SSL_NO_VERIFY = {{ _no_verify }}
 {%- endif %}
 
 # The CA certificate to use to verify SSL connections
diff --git a/horizon/files/horizon_settings/_local_settings.py b/horizon/files/horizon_settings/_local_settings.py
index 476272a..cc5d096 100644
--- a/horizon/files/horizon_settings/_local_settings.py
+++ b/horizon/files/horizon_settings/_local_settings.py
@@ -28,14 +28,6 @@
 
 CACHES = {
     'default': {
-{# TODO(vsaienko) remove this as we do not have custom cache backend starting from Pike #}
-{%- if server.version in ['mitaka', 'newton', 'ocata'] %}
-        'OPTIONS': {
-                'DEAD_RETRY': 1,
-                'SERVER_RETRIES': 1,
-                'SOCKET_TIMEOUT': 1,
-        },
-{%- endif %}
         'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
         {%- if app.cache.members is defined %}
         'LOCATION': "{%- for member in app.cache.members %}{{ member.host }}:{{ member.port }}{% if not loop.last %};{% endif %}{%- endfor %}"
diff --git a/horizon/files/horizon_settings/_websso_settings.py b/horizon/files/horizon_settings/_websso_settings.py
index 3baff8c..7864d9d 100644
--- a/horizon/files/horizon_settings/_websso_settings.py
+++ b/horizon/files/horizon_settings/_websso_settings.py
@@ -8,17 +8,38 @@
 LOGOUT_URL = {{ server.websso.logout_url }}
 {%- endif %}
 
+{%- if server.websso.login_redirect_url is defined %}
+LOGIN_REDIRECT_URL = {{ server.websso.login_redirect_url }}
+{%- endif %}
+
 WEBSSO_ENABLED = True
 
 WEBSSO_CHOICES = (
+{%- if server.websso.websso_choices is mapping %}
+  {%- for choice_name, choice in server.websso.websso_choices.iteritems() %}
+    ("{{ choice_name  }}", _("{{ choice.get('description') }}")),
+  {%- endfor %}
+{%- else %}
     ("credentials", _("Keystone Credentials")),
-    {%- if 'oidc' in server.websso.websso_choices %}
+  {%- for choice in server.websso.websso_choices %}
+    {%- if 'oidc' in choice %}
     ("oidc", _("OpenID Connect")),
     {%- endif %}
-    {%- if 'saml2' in server.websso.websso_choices %}
+    {%- if 'saml2' in choice %}
     ("saml2", _("Security Assertion Markup Language")),
     {%- endif %}
+  {%- endfor %}
+{%- endif %}
 )
 
 WEBSSO_INITIAL_CHOICE = "{{ server.websso.get('websso_initial_choice', 'credentials') }}"
+
+{%- if server.websso.idp_mapping is defined %}
+WEBSSO_IDP_MAPPING = {
+{%- for idp_name, idp in server.websso.idp_mapping.iteritems() %}
+    "{{ idp_name }}": ("{{ idp.get('id') }}", "{{ idp.get('protocol') }}"),
+{%- endfor %}
+}
+{%- endif %}
+
 {%- endif %}
diff --git a/horizon/files/local_settings/mitaka_settings.py b/horizon/files/local_settings/mitaka_settings.py
index be188b5..d161c42 100644
--- a/horizon/files/local_settings/mitaka_settings.py
+++ b/horizon/files/local_settings/mitaka_settings.py
@@ -1,4 +1,6 @@
 import os
+
+from django.utils.translation import pgettext_lazy
 from django.utils.translation import ugettext_lazy as _
 from openstack_dashboard import exceptions
 
@@ -29,16 +31,29 @@
     'disable_password_reveal': True,
     'password_autocomplete': 'off'
 }
-{%- if app.theme is defined or (app.plugin is defined and app.plugin.horizon_theme is defined) %}
-{%- if app.theme is defined %}
-CUSTOM_THEME_PATH = 'themes/{{ app.theme }}'
-{%- elif app.plugin.horizon_theme.theme_name is defined %}
-# Enable custom theme if it is present.
-try:
-  from openstack_dashboard.enabled._99_horizon_theme import CUSTOM_THEME_PATH
-except ImportError:
-  pass
-{%- endif %}
+{%- if app.themes is defined %}
+# 'key', 'label', 'path'
+{%- set theme_dir = app.themes.get('directory', 'themes') %}
+AVAILABLE_THEMES = [
+{%- for slug, theme in app.themes.get('available', {}).iteritems() %}
+  {%- if theme.get('enabled', True) %}
+    (
+        "{{ slug }}",
+        pgettext_lazy("{{ theme.description }}", "{{ theme.name }}"),
+        "{{ theme.get('path', theme_dir + '/' + slug ) }}"
+    ),
+  {%- endif %}
+{%- endfor %}
+]
+
+# The default theme if no cookie is present
+DEFAULT_THEME = '{{ app.themes.get("default", "default") }}'
+
+# Theme Static Directory
+THEME_COLLECTION_DIR = '{{ theme_dir }}'
+
+# Theme Cookie Name
+THEME_COOKIE_NAME = '{{ app.themes.get("cookie_name", "theme") }}'
 {%- endif %}
 
 INSTALLED_APPS = (
diff --git a/horizon/files/local_settings/newton_settings.py b/horizon/files/local_settings/newton_settings.py
index be188b5..d161c42 100644
--- a/horizon/files/local_settings/newton_settings.py
+++ b/horizon/files/local_settings/newton_settings.py
@@ -1,4 +1,6 @@
 import os
+
+from django.utils.translation import pgettext_lazy
 from django.utils.translation import ugettext_lazy as _
 from openstack_dashboard import exceptions
 
@@ -29,16 +31,29 @@
     'disable_password_reveal': True,
     'password_autocomplete': 'off'
 }
-{%- if app.theme is defined or (app.plugin is defined and app.plugin.horizon_theme is defined) %}
-{%- if app.theme is defined %}
-CUSTOM_THEME_PATH = 'themes/{{ app.theme }}'
-{%- elif app.plugin.horizon_theme.theme_name is defined %}
-# Enable custom theme if it is present.
-try:
-  from openstack_dashboard.enabled._99_horizon_theme import CUSTOM_THEME_PATH
-except ImportError:
-  pass
-{%- endif %}
+{%- if app.themes is defined %}
+# 'key', 'label', 'path'
+{%- set theme_dir = app.themes.get('directory', 'themes') %}
+AVAILABLE_THEMES = [
+{%- for slug, theme in app.themes.get('available', {}).iteritems() %}
+  {%- if theme.get('enabled', True) %}
+    (
+        "{{ slug }}",
+        pgettext_lazy("{{ theme.description }}", "{{ theme.name }}"),
+        "{{ theme.get('path', theme_dir + '/' + slug ) }}"
+    ),
+  {%- endif %}
+{%- endfor %}
+]
+
+# The default theme if no cookie is present
+DEFAULT_THEME = '{{ app.themes.get("default", "default") }}'
+
+# Theme Static Directory
+THEME_COLLECTION_DIR = '{{ theme_dir }}'
+
+# Theme Cookie Name
+THEME_COOKIE_NAME = '{{ app.themes.get("cookie_name", "theme") }}'
 {%- endif %}
 
 INSTALLED_APPS = (
diff --git a/horizon/files/local_settings/ocata_settings.py b/horizon/files/local_settings/ocata_settings.py
index be188b5..aebbc5e 100644
--- a/horizon/files/local_settings/ocata_settings.py
+++ b/horizon/files/local_settings/ocata_settings.py
@@ -1,4 +1,6 @@
 import os
+
+from django.utils.translation import pgettext_lazy
 from django.utils.translation import ugettext_lazy as _
 from openstack_dashboard import exceptions
 
@@ -29,16 +31,30 @@
     'disable_password_reveal': True,
     'password_autocomplete': 'off'
 }
-{%- if app.theme is defined or (app.plugin is defined and app.plugin.horizon_theme is defined) %}
-{%- if app.theme is defined %}
-CUSTOM_THEME_PATH = 'themes/{{ app.theme }}'
-{%- elif app.plugin.horizon_theme.theme_name is defined %}
-# Enable custom theme if it is present.
-try:
-  from openstack_dashboard.enabled._99_horizon_theme import CUSTOM_THEME_PATH
-except ImportError:
-  pass
-{%- endif %}
+
+{%- if app.themes is defined %}
+# 'key', 'label', 'path'
+{%- set theme_dir = app.themes.get('directory', 'themes') %}
+AVAILABLE_THEMES = [
+{%- for slug, theme in app.themes.get('available', {}).iteritems() %}
+  {%- if theme.get('enabled', True) %}
+    (
+        "{{ slug }}",
+        pgettext_lazy("{{ theme.description }}", "{{ theme.name }}"),
+        "{{ theme.get('path', theme_dir + '/' + slug ) }}"
+    ),
+  {%- endif %}
+{%- endfor %}
+]
+
+# The default theme if no cookie is present
+DEFAULT_THEME = '{{ app.themes.get("default", "default") }}'
+
+# Theme Static Directory
+THEME_COLLECTION_DIR = '{{ theme_dir }}'
+
+# Theme Cookie Name
+THEME_COOKIE_NAME = '{{ app.themes.get("cookie_name", "theme") }}'
 {%- endif %}
 
 INSTALLED_APPS = (
diff --git a/horizon/files/local_settings/queens_settings.py b/horizon/files/local_settings/queens_settings.py
index be188b5..d161c42 100644
--- a/horizon/files/local_settings/queens_settings.py
+++ b/horizon/files/local_settings/queens_settings.py
@@ -1,4 +1,6 @@
 import os
+
+from django.utils.translation import pgettext_lazy
 from django.utils.translation import ugettext_lazy as _
 from openstack_dashboard import exceptions
 
@@ -29,16 +31,29 @@
     'disable_password_reveal': True,
     'password_autocomplete': 'off'
 }
-{%- if app.theme is defined or (app.plugin is defined and app.plugin.horizon_theme is defined) %}
-{%- if app.theme is defined %}
-CUSTOM_THEME_PATH = 'themes/{{ app.theme }}'
-{%- elif app.plugin.horizon_theme.theme_name is defined %}
-# Enable custom theme if it is present.
-try:
-  from openstack_dashboard.enabled._99_horizon_theme import CUSTOM_THEME_PATH
-except ImportError:
-  pass
-{%- endif %}
+{%- if app.themes is defined %}
+# 'key', 'label', 'path'
+{%- set theme_dir = app.themes.get('directory', 'themes') %}
+AVAILABLE_THEMES = [
+{%- for slug, theme in app.themes.get('available', {}).iteritems() %}
+  {%- if theme.get('enabled', True) %}
+    (
+        "{{ slug }}",
+        pgettext_lazy("{{ theme.description }}", "{{ theme.name }}"),
+        "{{ theme.get('path', theme_dir + '/' + slug ) }}"
+    ),
+  {%- endif %}
+{%- endfor %}
+]
+
+# The default theme if no cookie is present
+DEFAULT_THEME = '{{ app.themes.get("default", "default") }}'
+
+# Theme Static Directory
+THEME_COLLECTION_DIR = '{{ theme_dir }}'
+
+# Theme Cookie Name
+THEME_COOKIE_NAME = '{{ app.themes.get("cookie_name", "theme") }}'
 {%- endif %}
 
 INSTALLED_APPS = (
diff --git a/metadata/service/server/cluster.yml b/metadata/service/server/cluster.yml
index cb27011..1600163 100644
--- a/metadata/service/server/cluster.yml
+++ b/metadata/service/server/cluster.yml
@@ -42,6 +42,19 @@
         host: ${_param:horizon_identity_host}
         encryption: ${_param:horizon_identity_encryption}
         endpoint_type: ${_param:horizon_identity_endpoint_type}
+      themes:
+        default: "default"
+        directory: "themes"
+        cookie_name: "theme"
+        available:
+          default:
+            name: "Default"
+            description: "Default style theme"
+            enabled: True
+          material:
+            name: "Material"
+            description: "Google's Material Design style theme"
+            enabled: True
       policy:
         identity:
           source: file
diff --git a/metadata/service/server/single.yml b/metadata/service/server/single.yml
index 0325076..a191165 100644
--- a/metadata/service/server/single.yml
+++ b/metadata/service/server/single.yml
@@ -40,6 +40,19 @@
         host: ${_param:horizon_identity_host}
         encryption: ${_param:horizon_identity_encryption}
         endpoint_type: ${_param:horizon_identity_endpoint_type}
+      themes:
+        default: "default"
+        directory: "themes"
+        cookie_name: "theme"
+        available:
+          default:
+            name: "Default"
+            description: "Default style theme"
+            enabled: True
+          material:
+            name: "Material"
+            description: "Google's Material Design style theme"
+            enabled: True
       policy:
         identity:
           source: file
diff --git a/tests/pillar/cluster.sls b/tests/pillar/cluster.sls
index 1f640cd..9eeab95 100644
--- a/tests/pillar/cluster.sls
+++ b/tests/pillar/cluster.sls
@@ -4,6 +4,7 @@
     version: liberty
     secret_key: secret
     session_timeout: 43200
+    ssl_no_verify: false
     wsgi:
       processes: 3
       threads: 10
@@ -35,13 +36,34 @@
     websso:
       login_url: "WEBROOT + 'auth/login/'"
       logout_url: "WEBROOT + 'auth/logout/'"
+      login_redirect_url: "WEBROOT + 'project/'"
       websso_choices:
-        - saml2
         - oidc
+        - saml2
+      idp_mapping:
+        myidp_openid:
+          id: myidp
+          protocol: openid
+        myipd_mapped:
+          id: myidp
+          protocol: mapped
     horizon_config:
       password_autocomplete: off
     openstack_neutron_network:
       enable_fip_topology_check: False
+    themes:
+      default: default
+      directory: themes
+      cookie_name: theme
+      available:
+        default:
+          name: "Default"
+          description: "Default style theme"
+          path: "themes/default"
+        material:
+          name: "Material"
+          description: "Google's Material Design style theme"
+          path: "themes/material"
 
 haproxy:
   proxy:
diff --git a/tests/pillar/single.sls b/tests/pillar/single.sls
index 0dbc3cb..08f6794 100644
--- a/tests/pillar/single.sls
+++ b/tests/pillar/single.sls
@@ -4,6 +4,7 @@
     version: liberty
     secret_key: secret
     session_timeout: 43200
+    ssl_no_verify: false
     bind:
       address: 127.0.0.1
       port: 80
@@ -33,12 +34,37 @@
     websso:
       login_url: "WEBROOT + 'auth/login/'"
       logout_url: "WEBROOT + 'auth/logout/'"
+      login_redirect_url: "WEBROOT + 'project/'"
       websso_choices:
-        - saml2
-        - oidc
+        credentials:
+          description: "Keystone Credentials"
+        oidc:
+          description: "OpenID Connect"
+        saml2:
+          description: "Security Assertion Markup Language"
+      idp_mapping:
+        myidp_openid:
+          id: myidp
+          protocol: openid
+        myipd_mapped:
+          id: myidp
+          protocol: mapped
     horizon_config:
       password_autocomplete: off
     openstack_neutron_network:
       enable_fip_topology_check: False
     default_domain: default
     multidomain: False
+    themes:
+      default: default
+      directory: themes
+      cookie_name: theme
+      available:
+        default:
+          name: "Default"
+          description: "Default style theme"
+          path: "themes/default"
+        material:
+          name: "Material"
+          description: "Google's Material Design style theme"
+          path: "themes/material"