diff -pruN 2.4.1-2/debian/changelog 3.5.0-2/debian/changelog
--- 2.4.1-2/debian/changelog	2016-10-04 08:50:47.000000000 +0000
+++ 3.5.0-2/debian/changelog	2017-10-28 21:25:04.000000000 +0000
@@ -1,45 +1,38 @@
-python-django-openstack-auth (2.4.1-2) unstable; urgency=medium
+python-django-openstack-auth (3.5.0-2) unstable; urgency=medium
 
-  * Uploading to unstable.
+  * Uploading to unstable:
+    - Works with Django 1.11 (Closes: #865799).
 
- -- Thomas Goirand <zigo@debian.org>  Tue, 04 Oct 2016 10:50:47 +0200
+ -- Thomas Goirand <zigo@debian.org>  Sat, 28 Oct 2017 21:25:04 +0000
 
-python-django-openstack-auth (2.4.1-1) experimental; urgency=medium
-
-  [ Ondřej Nový ]
-  * d/s/options: extend-diff-ignore of .gitreview
-  * d/control: Using OpenStack's Gerrit as VCS URLs.
-
-  [ Thomas Goirand ]
-  * New upstream release.
-  * Fixed (build-)depends for this release.
-  * Add Add-is_authenticated-and-is_anonymous-properties.patch.
-
- -- Thomas Goirand <zigo@debian.org>  Wed, 28 Sep 2016 14:54:49 +0200
-
-python-django-openstack-auth (2.3.0-2) experimental; urgency=medium
-
-  * Fix FTBFS with Django 1.10 (Closes: #828670).
-  * Using pkgos-dh_auto_install from openstack-pkg-tools >= 52~.
-  * Testing using Py3 as well.
-
- -- Thomas Goirand <zigo@debian.org>  Tue, 19 Jul 2016 07:28:10 +0000
-
-python-django-openstack-auth (2.3.0-1) experimental; urgency=medium
+python-django-openstack-auth (3.5.0-1) experimental; urgency=medium
 
   [ Ondřej Nový ]
   * Standards-Version is 3.9.8 now (no change)
   * d/rules: Changed UPSTREAM_GIT protocol to https
 
-  [ Corey Bryant ]
-  * New upstream release.
-  * d/gbp.conf: Update branch for Newton.
-  * d/control: Align (Build-)Depends with upstream.
+  [ Daniel Baumann ]
+  * Updating vcs fields.
+  * Updating copyright format url.
+  * Updating maintainer field.
+  * Running wrap-and-sort -bast.
+  * Updating standards version to 4.0.0.
+  * Removing gbp.conf, not used anymore or should be specified in the
+    developers dotfiles.
+  * Updating standards version to 4.0.1.
+  * Deprecating priority extra as per policy 4.0.1.
+  * Prefixing all debhelper files.
+  * Updating standards version to 4.1.0.
 
   [ Thomas Goirand ]
-  * Add Corey Bryant <corey.bryant@canonical.com> to uploaders.
+  * New upstream release.
+  * Fixed (build-)depends for this release.
+  * Using pkgos-dh_auto_install.
+  * Removed transition package.
+  * Switch to debhelper 10.
+  * Running tests also with Python 3.
 
- -- Thomas Goirand <zigo@debian.org>  Tue, 14 Jun 2016 15:37:45 +0000
+ -- Thomas Goirand <zigo@debian.org>  Thu, 12 Oct 2017 23:33:44 +0200
 
 python-django-openstack-auth (2.2.1-1) unstable; urgency=medium
 
diff -pruN 2.4.1-2/debian/compat 3.5.0-2/debian/compat
--- 2.4.1-2/debian/compat	2016-10-04 08:50:47.000000000 +0000
+++ 3.5.0-2/debian/compat	2017-10-28 21:25:04.000000000 +0000
@@ -1 +1 @@
-9
+10
diff -pruN 2.4.1-2/debian/control 3.5.0-2/debian/control
--- 2.4.1-2/debian/control	2016-10-04 08:50:47.000000000 +0000
+++ 3.5.0-2/debian/control	2017-10-28 21:25:04.000000000 +0000
@@ -1,63 +1,69 @@
 Source: python-django-openstack-auth
 Section: python
 Priority: optional
-Maintainer: PKG OpenStack <openstack-devel@lists.alioth.debian.org>
-Uploaders: Thomas Goirand <zigo@debian.org>,
-           Ivan Udovichenko <iudovichenko@mirantis.com>,
-           Corey Bryant <corey.bryant@canonical.com>,
-Build-Depends: debhelper (>= 9),
-               dh-python,
-               openstack-pkg-tools (>= 52~),
-               python-all,
-               python-pbr (>= 1.8),
-               python-setuptools,
-               python-sphinx,
-               python3-all,
-               python3-pbr (>= 1.8),
-               python3-setuptools,
-Build-Depends-Indep: python-babel (>= 2.3.4),
-                     python-coverage,
-                     python-django (>= 1.8),
-                     python-hacking (>= 0.10.0),
-                     python-keystoneauth1 (>= 2.10.0),
-                     python-keystoneclient (>= 1:1.7.0),
-                     python-mock (>= 1.3),
-                     python-mox3,
-                     python-oslo.config (>= 1:3.14.0),
-                     python-oslo.policy (>= 1.9.0),
-                     python-oslosphinx (>= 2.5.0),
-                     python-six (>= 1.9.0),
-                     python-testscenarios,
-                     python3-babel (>= 2.3.4),
-                     python3-django (>= 1.8),
-                     python3-keystoneauth1 (>= 2.10.0),
-                     python3-keystoneclient (>= 1:1.7.0),
-                     python3-mock (>= 1.3),
-                     python3-mox3,
-                     python3-oslo.config (>= 1:3.14.0),
-                     python3-oslo.policy (>= 1.9.0),
-                     python3-six (>= 1.9.0),
-                     python3-testscenarios,
-Standards-Version: 3.9.8
-Vcs-Git: https://git.openstack.org/openstack/deb-python-django-openstack-auth -b debian/newton
-Vcs-Browser: https://git.openstack.org/cgit/openstack/deb-python-django-openstack-auth?h=debian%2Fnewton
+Maintainer: Debian OpenStack <openstack-devel@lists.alioth.debian.org>
+Uploaders:
+ Thomas Goirand <zigo@debian.org>,
+ Ivan Udovichenko <iudovichenko@mirantis.com>,
+Build-Depends:
+ debhelper (>= 10),
+ dh-python,
+ openstack-pkg-tools,
+ python-all,
+ python-pbr (>= 2.0.0),
+ python-setuptools,
+ python-sphinx (>= 1.6.2),
+ python3-all,
+ python3-pbr (>= 2.0.0),
+ python3-setuptools,
+Build-Depends-Indep:
+ python-babel,
+ python-coverage,
+ python-django,
+ python-hacking,
+ python-keystoneauth1 (>= 2.21.0),
+ python-keystoneclient (>= 1:3.8.0),
+ python-mock,
+ python-mox3,
+ python-openstackdocstheme (>= 1.11.0),
+ python-oslo.config (>= 1:4.0.0),
+ python-oslo.policy (>= 1.23.0),
+ python-six,
+ python-testscenarios,
+ python3-babel,
+ python3-django,
+ python3-keystoneauth1 (>= 2.21.0),
+ python3-keystoneclient (>= 1:3.8.0),
+ python3-mock,
+ python3-mox3,
+ python3-oslo.config (>= 1:4.0.0),
+ python3-oslo.policy (>= 1.23.0),
+ python3-six,
+ python3-testscenarios,
+Standards-Version: 4.1.0
+Vcs-Git: https://anonscm.debian.org/git/openstack/python/python-django-openstack-auth.git
+Vcs-Browser: https://anonscm.debian.org/cgit/openstack/python/python-django-openstack-auth.git
 Homepage: https://pypi.python.org/pypi/django_openstack_auth
 
 Package: python-django-openstack-auth
 Architecture: all
-Depends: python-django (>= 1.8),
-         python-keystoneclient (>= 1:1.7.0),
-         python-oslo.config (>= 1:3.14.0),
-         python-oslo.policy (>= 1.9.0),
-         python-pbr (>= 1.8),
-         python-six (>= 1.9.0),
-         python3-keystoneauth1 (>= 2.10.0),
-         ${misc:Depends},
-         ${python:Depends},
-Breaks: python-openstack-auth (<= 2.0.0-2),
-Replaces: python-openstack-auth (<= 2.0.0-2),
-Provides: python-openstack-auth,
-          ${python:Provides},
+Depends:
+ python-django,
+ python-keystoneauth1 (>= 2.21.0),
+ python-keystoneclient (>= 1:3.8.0),
+ python-oslo.config (>= 1:4.0.0),
+ python-oslo.policy (>= 1.23.0),
+ python-pbr (>= 2.0.0),
+ python-six,
+ ${misc:Depends},
+ ${python:Depends},
+Breaks:
+ python-openstack-auth (<= 2.0.0-2),
+Replaces:
+ python-openstack-auth (<= 2.0.0-2),
+Provides:
+ python-openstack-auth,
+ ${python:Provides},
 Description: Django authentication backend for Openstack - Python 2.7
  Django authentication backend for use with the OpenStack Keystone Identity
  backend. This Python module is used by Horizon (the Openstack Dashport web
@@ -65,32 +71,20 @@ Description: Django authentication backe
  .
  This package provides the Python 2.7 module.
 
-Package: python-openstack-auth
-Section: oldlibs
-Priority: extra
-Architecture: all
-Depends: python-django-openstack-auth,
-         ${misc:Depends},
-Description: Django authentication backend for Openstack - transition package
- Django authentication backend for use with the OpenStack Keystone Identity
- backend. This Python module is used by Horizon (the Openstack Dashport web
- GUI) to auth against Keystone.
- .
- This is a transitional package to upgrade to python-django-openstack-auth,
- and it is safe to remove.
-
 Package: python3-django-openstack-auth
 Architecture: all
-Depends: python3-django (>= 1.8),
-         python3-keystoneauth1 (>= 2.10.0),
-         python3-keystoneclient (>= 1:1.7.0),
-         python3-oslo.config (>= 1:3.14.0),
-         python3-oslo.policy (>= 1.9.0),
-         python3-pbr (>= 1.8),
-         python3-six (>= 1.9.0),
-         ${misc:Depends},
-         ${python3:Depends},
-Provides: ${python3:Provides},
+Depends:
+ python3-django,
+ python3-keystoneauth1 (>= 2.21.0),
+ python3-keystoneclient (>= 1:3.8.0),
+ python3-oslo.config (>= 1:4.0.0),
+ python3-oslo.policy (>= 1.23.0),
+ python3-pbr (>= 2.0.0),
+ python3-six,
+ ${misc:Depends},
+ ${python3:Depends},
+Provides:
+ ${python3:Provides},
 Description: Django authentication backend for Openstack - Python 3.x
  Django authentication backend for use with the OpenStack Keystone Identity
  backend. This Python module is used by Horizon (the Openstack Dashport web
diff -pruN 2.4.1-2/debian/copyright 3.5.0-2/debian/copyright
--- 2.4.1-2/debian/copyright	2016-10-04 08:50:47.000000000 +0000
+++ 3.5.0-2/debian/copyright	2017-10-28 21:25:04.000000000 +0000
@@ -1,4 +1,4 @@
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 Upstream-Name: python-django-openstack-auth
 Source: https://github.com/openstack/django_openstack_auth.git
 
diff -pruN 2.4.1-2/debian/docs 3.5.0-2/debian/docs
--- 2.4.1-2/debian/docs	2016-10-04 08:50:47.000000000 +0000
+++ 3.5.0-2/debian/docs	1970-01-01 00:00:00.000000000 +0000
@@ -1 +0,0 @@
-README.rst
diff -pruN 2.4.1-2/debian/gbp.conf 3.5.0-2/debian/gbp.conf
--- 2.4.1-2/debian/gbp.conf	2016-10-04 08:50:47.000000000 +0000
+++ 3.5.0-2/debian/gbp.conf	1970-01-01 00:00:00.000000000 +0000
@@ -1,8 +0,0 @@
-[DEFAULT]
-upstream-branch = master
-debian-branch = debian/newton
-upstream-tag = %(version)s
-compression = xz
-
-[buildpackage]
-export-dir = ../build-area/
diff -pruN 2.4.1-2/debian/patches/Add-is_authenticated-and-is_anonymous-properties.patch 3.5.0-2/debian/patches/Add-is_authenticated-and-is_anonymous-properties.patch
--- 2.4.1-2/debian/patches/Add-is_authenticated-and-is_anonymous-properties.patch	2016-10-04 08:50:47.000000000 +0000
+++ 3.5.0-2/debian/patches/Add-is_authenticated-and-is_anonymous-properties.patch	1970-01-01 00:00:00.000000000 +0000
@@ -1,117 +0,0 @@
-Description: Add is_authenticated and is_anonymous properties
- See
- https://docs.djangoproject.com/en/1.10/releases/1.10/#using-user-is-authenticated-and-user-is-anonymous-as-methods
- .
- is_anonymous() and is_authenticated() functions are now properties, and
- throw critical security warnings when using python manage.py check in
- django 1.10
- .
- The duplication is just to make it explicit which code paths are being
- followed. They could be refactored to remove it, but in a few months
- when we move to the next LTS we would just end up removing the refactors
- since there would once again be a single path.
- .
- We also removed the `margin` parameter, since it is never used anywhere.
- This will be documented in a Horizon release note.
-Author: Rob Cresswell <robert.cresswell@outlook.com>
-Date: Wed, 10 Aug 2016 09:10:20 +0100
-Change-Id: I7a92089ae62a9017274002648f26f13bc34709d9
-Origin: upstream, https://review.openstack.org/374732
-Last-Update: 2016-09-27
-
-diff --git a/openstack_auth/user.py b/openstack_auth/user.py
-index c9200f4..fba75e7 100644
---- a/openstack_auth/user.py
-+++ b/openstack_auth/user.py
-@@ -14,9 +14,11 @@
- import hashlib
- import logging
- 
-+import django
- from django.conf import settings
- from django.contrib.auth import models
- from django.db import models as db_models
-+from django.utils import deprecation
- from keystoneauth1 import exceptions as keystone_exceptions
- from keystoneclient.common import cms as keystone_cms
- import six
-@@ -261,35 +263,50 @@ def is_token_expired(self, margin=None):
-             return None
-         return not utils.is_token_valid(self.token, margin)
- 
--    def is_authenticated(self, margin=None):
--        """Checks for a valid authentication.
--
--        :param margin:
--           A security time margin in seconds before end of authentication.
--           Will return ``False`` if authentication ends in less than ``margin``
--           seconds of time.
--           A default margin can be set by the TOKEN_TIMEOUT_MARGIN in the
--           django settings.
--
--        """
--        return (self.token is not None and
--                utils.is_token_valid(self.token, margin))
--
--    def is_anonymous(self, margin=None):
--        """Return if the user is not authenticated.
--
--        Returns ``True`` if not authenticated,``False`` otherwise.
--
--        :param margin:
--           A security time margin in seconds before end of an eventual
--           authentication.
--           Will return ``True`` even if authenticated but that authentication
--           ends in less than ``margin`` seconds of time.
--           A default margin can be set by the TOKEN_TIMEOUT_MARGIN in the
--           django settings.
--
--        """
--        return not self.is_authenticated(margin)
-+    if django.VERSION >= (1, 10):
-+        @property
-+        def is_authenticated(self):
-+            """Checks for a valid authentication."""
-+            if (self.token is not None and utils.is_token_valid(self.token)):
-+                return deprecation.CallableTrue
-+            else:
-+                return deprecation.CallableFalse
-+
-+        @property
-+        def is_anonymous(self):
-+            """Return if the user is not authenticated.
-+
-+            Returns ``True`` if not authenticated,``False`` otherwise.
-+            """
-+            return deprecation.CallableBool(not self.is_authenticated)
-+    else:
-+        def is_authenticated(self, margin=None):
-+            """Checks for a valid authentication.
-+
-+            :param margin:
-+               A security time margin in seconds before end of authentication.
-+               Will return ``False`` if authentication ends in less than
-+               ``margin`` seconds of time.
-+               A default margin can be set by the TOKEN_TIMEOUT_MARGIN in the
-+               django settings.
-+            """
-+            return (self.token is not None and
-+                    utils.is_token_valid(self.token, margin))
-+
-+        def is_anonymous(self, margin=None):
-+            """Return if the user is not authenticated.
-+
-+            Returns ``True`` if not authenticated,``False`` otherwise.
-+
-+            :param margin:
-+               A security time margin in seconds before end of an eventual
-+               authentication.
-+               Will return ``True`` even if authenticated but that
-+               authentication ends in less than ``margin`` seconds of time.
-+               A default margin can be set by the TOKEN_TIMEOUT_MARGIN in the
-+               django settings.
-+            """
-+            return not self.is_authenticated(margin)
- 
-     @property
-     def is_active(self):
diff -pruN 2.4.1-2/debian/patches/series 3.5.0-2/debian/patches/series
--- 2.4.1-2/debian/patches/series	2016-10-04 08:50:47.000000000 +0000
+++ 3.5.0-2/debian/patches/series	1970-01-01 00:00:00.000000000 +0000
@@ -1 +0,0 @@
-Add-is_authenticated-and-is_anonymous-properties.patch
diff -pruN 2.4.1-2/debian/python-django-openstack-auth.docs 3.5.0-2/debian/python-django-openstack-auth.docs
--- 2.4.1-2/debian/python-django-openstack-auth.docs	1970-01-01 00:00:00.000000000 +0000
+++ 3.5.0-2/debian/python-django-openstack-auth.docs	2017-10-28 21:25:04.000000000 +0000
@@ -0,0 +1 @@
+README.rst
diff -pruN 2.4.1-2/debian/source/options 3.5.0-2/debian/source/options
--- 2.4.1-2/debian/source/options	2016-10-04 08:50:47.000000000 +0000
+++ 3.5.0-2/debian/source/options	2017-10-28 21:25:04.000000000 +0000
@@ -1,2 +1 @@
 extend-diff-ignore = "^[^/]*[.]egg-info/"
-extend-diff-ignore = "^[.]gitreview$"
diff -pruN 2.4.1-2/doc/source/configuration/index.rst 3.5.0-2/doc/source/configuration/index.rst
--- 2.4.1-2/doc/source/configuration/index.rst	1970-01-01 00:00:00.000000000 +0000
+++ 3.5.0-2/doc/source/configuration/index.rst	2017-07-20 14:16:34.000000000 +0000
@@ -0,0 +1,425 @@
+=============
+Configuration
+=============
+
+Django OpenStack Auth is configured through Django ``settings.py`` file.
+In most cases it is used combined with the OpenStack Dashboard,
+so the settings file will be ``local/local_settings.py`` file
+in your OpenStack Dashboard deployment.
+
+This page covers the configuration options referred by Django OpenStack Auth.
+
+:ref:`Some settings <settings-shared-with-horizon>` are also referred to
+by Horizon. Configure them carefully.
+
+General settings
+================
+
+``AUTHENTICATION_PLUGINS``
+--------------------------
+
+Default: ``['openstack_auth.plugin.password.PasswordPlugin', 'openstack_auth.plugin.token.TokenPlugin']``
+
+A list of authentication plugins to be used.
+In most cases, there is no need to configure this.
+
+``AVAILABLE_REGIONS``
+---------------------
+
+Default: ``None``
+
+A list of tuples which define multiple regions. The tuple format is
+``('http://{{ keystone_host }}:5000/v2.0', '{{ region_name }}')``. If any regions
+are specified the login form will have a dropdown selector for authenticating
+to the appropriate region, and there will be a region switcher dropdown in
+the site header when logged in.
+
+You should also define ``OPENSTACK_KEYSTONE_URL`` to indicate which of
+the regions is the default one.
+
+
+``DEFAULT_SERVICE_REGIONS``
+---------------------------
+
+Default: ``{}``
+
+The default service region is set on a per-endpoint basis, meaning that once
+the user logs into some Keystone endpoint, if a default service region is
+defined for it in this setting and exists within Keystone catalog, it will be
+set as the initial service region in this endpoint. By default it is an empty
+dictionary because upstream can neither predict service region names in a
+specific deployment, nor tell whether this behavior is desired. The key of the
+dictionary is a full url of a Keystone endpoint with version suffix, the value
+is a region name.
+
+Example::
+
+    DEFAULT_SERVICE_REGIONS = {
+        OPENSTACK_KEYSTONE_URL: 'RegionOne'
+    }
+
+
+``OPENSTACK_API_VERSIONS``
+--------------------------
+
+Default::
+
+    {
+        "identity": 2.0,
+        ...,
+    }
+
+Overrides for OpenStack API versions. Use this setting to force the
+OpenStack dashboard to use a specific API version for a given service API.
+Django OpenStack Auth refers to only the ``"identity"`` entry.
+The current valid values are "2.0" or "3".
+
+.. note::
+
+   See `Horizon settings
+   <https://docs.openstack.org/developer/horizon/install/settings.html#openstack-api-versions>`__
+   for the full description of this setting.
+
+``OPENSTACK_ENDPOINT_TYPE``
+---------------------------
+
+Default: ``"publicURL"``
+
+A string which specifies the endpoint type to use for the endpoints in the
+Keystone service catalog. The default value for all services except for
+identity is ``"publicURL"``. The default value for the identity service is
+``"internalURL"``.
+
+``OPENSTACK_KEYSTONE_ADMIN_ROLES``
+----------------------------------
+
+Default: ``["admin"]``
+
+The list of roles that have administrator privileges in this OpenStack
+installation. This check is very basic and essentially only works with
+keystone v2.0 and v3 with the default policy file. The setting assumes there
+is a common ``admin`` like role(s) across services. Example uses of this
+setting are:
+
+* to rename the ``admin`` role to ``cloud-admin``
+* allowing multiple roles to have administrative privileges, like
+  ``["admin", "cloud-admin", "net-op"]``
+
+``OPENSTACK_KEYSTONE_DEFAULT_DOMAIN``
+-------------------------------------
+
+Default: ``"Default"``
+
+Overrides the default domain used when running on single-domain model
+with Keystone V3. All entities will be created in the default domain.
+
+.. note::
+
+   This value must be the name of the default domain, NOT the ID.
+   Also, you will most likely have a value in the keystone policy file like
+   ``"cloud_admin": "rule:admin_required and domain_id:<your domain id>"``.
+   This value must be the name of the domain whose ID is specified there.
+
+``OPENSTACK_KEYSTONE_DOMAIN_CHOICES``
+-------------------------------------
+
+.. versionadded:: 12.0.0(Pike)
+
+Default::
+
+        (
+          ('Default', 'Default'),
+        )
+
+If OPENSTACK_KEYSTONE_DOMAIN_DROPDOWN is enabled, this option can be used to
+set the available domains to choose from. This is a list of pairs whose first
+value is the domain name and the second is the display name.
+
+``OPENSTACK_KEYSTONE_DOMAIN_DROPDOWN``
+--------------------------------------
+
+.. versionadded:: 12.0.0(Pike)
+
+Default: ``False``
+Set this to True if you want available domains displayed as a dropdown menu on
+the login screen. It is strongly advised NOT to enable this for public clouds,
+as advertising enabled domains to unauthenticated customers irresponsibly
+exposes private information. This should only be used for private clouds where
+the dashboard sits behind a corporate firewall.
+
+``OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT``
+------------------------------------------
+
+Default: ``False``
+
+Set this to True if running on multi-domain model. When this is enabled, it
+will require user to enter the Domain name in addition to username for login.
+
+``OPENSTACK_KEYSTONE_URL``
+--------------------------
+
+Default: ``"http://%s:5000/v2.0" % OPENSTACK_HOST``
+
+The full URL for the Keystone endpoint used for authentication. Unless you
+are using HTTPS, running your Keystone server on a nonstandard port, or using
+a nonstandard URL scheme you shouldn't need to touch this setting.
+
+``OPENSTACK_SSL_CACERT``
+------------------------
+
+Default: ``None``
+
+When unset or set to ``None`` the default CA certificate on the system is used
+for SSL verification.
+
+When set with the path to a custom CA certificate file, this overrides use of
+the default system CA certificate. This custom certificate is used to verify all
+connections to openstack services when making API calls.
+
+``OPENSTACK_SSL_NO_VERIFY``
+---------------------------
+
+Default: ``False``
+
+Disable SSL certificate checks in the OpenStack clients (useful for self-signed
+certificates).
+
+``OPENSTACK_TOKEN_HASH_ALGORITHM``
+----------------------------------
+
+Default: ``"md5"``
+
+The hash algorithm to use for authentication tokens. This must match the hash
+algorithm that the identity (Keystone) server and the auth_token middleware
+are using. Allowed values are the algorithms supported by Python's hashlib
+library.
+
+``OPENSTACK_TOKEN_HASH_ENABLED``
+--------------------------------
+
+(Deprecated)
+
+Default: ``True``
+
+Hashing tokens from Keystone keeps the Horizon session data smaller, but it
+doesn't work in some cases when using PKI tokens.  Uncomment this value and
+set it to False if using PKI tokens and there are 401 errors due to token
+hashing.
+
+This option is now marked as "deprecated" and will be removed in Ocata or a
+later release. PKI tokens currently work with hashing, and Keystone will soon
+deprecate usage of PKI tokens.
+
+``PASSWORD_EXPIRES_WARNING_THRESHOLD_DAYS``
+-------------------------------------------
+
+Default: ``-1``
+
+Password will have an expiration date when using keystone v3 and enabling the
+feature. This setting allows you to set the number of days that the user will
+be alerted prior to the password expiration. Once the password expires keystone
+will deny the access and users must contact an admin to change their password.
+Setting this value to ``N`` days means the user will be alerted when the
+password expires in less than ``N+1`` days. ``-1`` disables the feature.
+
+``POLICY_FILES``
+----------------
+
+Default: ``{'identity': 'keystone_policy.json', 'compute': 'nova_policy.json'}``
+
+This should essentially be the mapping of the contents of ``POLICY_FILES_PATH``
+to service types.  When policy.json files are added to ``POLICY_FILES_PATH``,
+they should be included here too.
+
+``POLICY_FILES_PATH``
+---------------------
+
+Default:  ``os.path.join(ROOT_PATH, "conf")``
+
+Specifies where service based policy files are located. These are used to
+define the policy rules actions are verified against.
+
+``SECURE_PROXY_ADDR_HEADER``
+----------------------------
+
+Default: ``False``
+
+If horizon is behind a proxy server and the proxy is configured, the IP address
+from request is passed using header variables inside the request. The header
+name depends on a proxy or a load-balancer. This setting specifies the name of
+the header with remote IP address. The main use is for authentication log
+(success or fail) displaing the IP address of the user.
+The commom value for this setting is ``HTTP_X_REAL_IP`` or
+``HTTP_X_FORWARDED_FOR``.
+If not present, then ``REMOTE_ADDR`` header is used. (``REMOTE_ADDR`` is the
+field of Django HttpRequest object which contains IP address of the client.)
+
+``SESSION_TIMEOUT``
+-------------------
+
+Default: ``"3600"``
+
+This ``SESSION_TIMEOUT`` is a method to supercede the token timeout with a
+shorter horizon session timeout (in seconds).  So if your token expires in
+60 minutes, a value of 1800 will log users out after 30 minutes.
+
+``TOKEN_DELETION_DISABLED``
+---------------------------
+
+Default: ``False``
+
+This setting allows deployers to control whether a token is deleted on log out.
+This can be helpful when there are often long running processes being run
+in the Horizon environment.
+
+``TOKEN_TIMEOUT_MARGIN``
+------------------------
+
+Default: ``0``
+
+A time margin in seconds to subtract from the real token's validity.
+An example usage is that the token can be valid once the middleware
+passed, and invalid (timed-out) during a view rendering and this
+generates authorization errors during the view rendering.
+By setting this value to some smaller seconds, you can avoid token
+expiration during a view rendering.
+
+``WEBROOT``
+-----------
+
+Default: ``"/"``
+
+Specifies the location where the access to the dashboard is configured in
+the web server.
+
+For example, if you're accessing the Dashboard via
+https://<your server>/dashboard, you would set this to ``"/dashboard/"``.
+
+.. note::
+
+    Additional settings may be required in the config files of your webserver
+    of choice. For example to make ``"/dashboard/"`` the web root in Apache,
+    the ``"sites-available/horizon.conf"`` requires a couple of additional
+    aliases set::
+
+        Alias /dashboard/static %HORIZON_DIR%/static
+
+        Alias /dashboard/media %HORIZON_DIR%/openstack_dashboard/static
+
+    Apache also requires changing your WSGIScriptAlias to reflect the desired
+    path.  For example, you'd replace ``/`` with ``/dashboard`` for the
+    alias.
+
+Web SSO (Single Sign On) settings
+=================================
+
+``WEBSSO_ENABLED``
+------------------
+
+Default: ``False``
+
+Enables keystone web single-sign-on if set to True. For this feature to work,
+make sure that you are using Keystone V3 and Django OpenStack Auth V1.2.0 or
+later.
+
+``WEBSSO_INITIAL_CHOICE``
+-------------------------
+
+Default: ``"credentials"``
+
+Determines the default authentication mechanism. When user lands on the login
+page, this is the first choice they will see.
+
+``WEBSSO_CHOICES``
+------------------
+
+Default::
+
+        (
+          ("credentials", _("Keystone Credentials")),
+          ("oidc", _("OpenID Connect")),
+          ("saml2", _("Security Assertion Markup Language"))
+        )
+
+This is the list of authentication mechanisms available to the user. It
+includes Keystone federation protocols such as OpenID Connect and SAML, and
+also keys that map to specific identity provider and federation protocol
+combinations (as defined in ``WEBSSO_IDP_MAPPING``). The list of choices is
+completely configurable, so as long as the id remains intact. Do not remove
+the credentials mechanism unless you are sure. Once removed, even admins will
+have no way to log into the system via the dashboard.
+
+``WEBSSO_IDP_MAPPING``
+----------------------
+
+Default: ``{}``
+
+A dictionary of specific identity provider and federation protocol combinations.
+From the selected authentication mechanism, the value will be looked up as keys
+in the dictionary. If a match is found, it will redirect the user to a identity
+provider and federation protocol specific WebSSO endpoint in keystone, otherwise
+it will use the value as the protocol_id when redirecting to the WebSSO by
+protocol endpoint.
+
+Example::
+
+        WEBSSO_CHOICES =  (
+            ("credentials", _("Keystone Credentials")),
+            ("oidc", _("OpenID Connect")),
+            ("saml2", _("Security Assertion Markup Language")),
+            ("acme_oidc", "ACME - OpenID Connect"),
+            ("acme_saml2", "ACME - SAML2")
+        )
+
+        WEBSSO_IDP_MAPPING = {
+            "acme_oidc": ("acme", "oidc"),
+            "acme_saml2": ("acme", "saml2")
+        }
+
+.. note::
+  The value is expected to be a tuple formatted as: (<idp_id>, <protocol_id>).
+
+K2K (Keystone to Keystone) Federation settings
+==============================================
+
+``KEYSTONE_PROVIDER_IDP_NAME``
+------------------------------
+
+Default: ``Local Keystone``
+
+The Keystone Provider drop down uses Keystone to Keystone federation
+to switch between Keystone service providers.
+This sets display name for Identity Provider (dropdown display name).
+
+``KEYSTONE_PROVIDER_IDP_ID``
+----------------------------
+
+Default:: ``localkeystone``
+
+This ID is used for only for comparison with the service provider IDs.
+This ID should not match any service provider IDs.
+
+.. _settings-shared-with-horizon:
+
+Settings shared with Horizon
+============================
+
+The following settings in Django OpenStack Auth are also used by Horizon.
+
+* ``AVAILABLE_REGIONS``
+* ``OPENSTACK_API_VERSIONS``
+* ``OPENSTACK_KEYSTONE_URL``
+* ``OPENSTACK_ENDPOINT_TYPE``
+* ``OPENSTACK_SSL_CACERT``
+* ``OPENSTACK_SSL_NO_VERIFY``
+* ``WEBROOT``
+
+Django OpenStack Auth also refers to the following Django settings.
+For more detail, see `Django settings documentation
+<https://docs.djangoproject.com/en/1.11/ref/settings/#auth>`__.
+They are usually configured as part of Horizon settings.
+
+* ``LOGIN_REDIRECT_URL``
+* ``LOGIN_URL``
+* ``SESSION_ENGINE``
+* ``USE_TZ``
diff -pruN 2.4.1-2/doc/source/conf.py 3.5.0-2/doc/source/conf.py
--- 2.4.1-2/doc/source/conf.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/doc/source/conf.py	2017-07-20 14:16:34.000000000 +0000
@@ -13,8 +13,12 @@
 
 import os
 
+import django
+
 os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openstack_auth.tests.settings')
 
+django.setup()
+
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
@@ -27,7 +31,13 @@ os.environ.setdefault('DJANGO_SETTINGS_M
 
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'oslosphinx']
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'openstackdocstheme']
+
+# openstackdocstheme options
+repository_name = 'openstack/django_openstack_auth'
+bug_project = 'django-openstack-auth'
+bug_tag = ''
+html_last_updated_fmt = '%Y-%m-%d %H:%M'
 
 # Add any paths that contain templates here, relative to this directory.
 templates_path = ['_templates']
@@ -84,7 +94,7 @@ modindex_common_prefix = ['openstack_aut
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'default'
+html_theme = 'openstackdocs'
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
@@ -113,7 +123,7 @@ html_theme = 'default'
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+#html_static_path = []
 
 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
 # using the given strftime format.
@@ -158,78 +168,3 @@ html_static_path = ['_static']
 
 # Output file base name for HTML help builder.
 htmlhelp_basename = 'DjangoOpenStackAuthdoc'
-
-
-# -- Options for LaTeX output --------------------------------------------------
-
-latex_elements = {
-    # The paper size ('letterpaper' or 'a4paper').
-    #'papersize': 'letterpaper',
-
-    # The font size ('10pt', '11pt' or '12pt').
-    #'pointsize': '10pt',
-
-    # Additional stuff for the LaTeX preamble.
-    #'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title, author, documentclass [howto/manual]).
-latex_documents = [
-  ('index', 'DjangoOpenStackAuth.tex', u'Django OpenStack Auth Documentation',
-   u'Gabriel Hurley', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output --------------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
-    ('index', 'djangoopenstackauth', u'Django OpenStack Auth Documentation',
-     [u'Gabriel Hurley'], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output ------------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-#  dir menu entry, description, category)
-texinfo_documents = [
-  ('index', 'DjangoOpenStackAuth', u'Django OpenStack Auth Documentation',
-   u'Gabriel Hurley', 'DjangoOpenStackAuth', 'One line description of project.',
-   'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
diff -pruN 2.4.1-2/doc/source/index.rst 3.5.0-2/doc/source/index.rst
--- 2.4.1-2/doc/source/index.rst	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/doc/source/index.rst	2017-07-20 14:16:34.000000000 +0000
@@ -11,15 +11,9 @@ The current version is designed to work
 .. toctree::
    :maxdepth: 2
 
-   installation
-   ref/user
-   ref/views
-   ref/forms
-   ref/backend
-   ref/utils
-
-Indices and tables
-==================
+   install/index
+   configuration/index
+   reference/index
 
 * :ref:`genindex`
 * :ref:`modindex`
diff -pruN 2.4.1-2/doc/source/install/index.rst 3.5.0-2/doc/source/install/index.rst
--- 2.4.1-2/doc/source/install/index.rst	1970-01-01 00:00:00.000000000 +0000
+++ 3.5.0-2/doc/source/install/index.rst	2017-07-20 14:16:34.000000000 +0000
@@ -0,0 +1,63 @@
+===============
+Getting Started
+===============
+
+Installation
+============
+
+Installing is quick and easy:
+
+#. Run ``pip install django_openstack_auth``.
+
+#. Add ``openstack_auth`` to ``settings.INSTALLED_APPS``.
+
+#. Add ``'openstack_auth.backend.KeystoneBackend'`` to your
+   ``settings.AUTHENTICATION_BACKENDS``, e.g.::
+
+        AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',)
+
+#. Configure your API endpoint(s) in ``settings.py``::
+
+        OPENSTACK_KEYSTONE_URL = "http://example.com:5000/v3"
+
+#. Include ``'openstack_auth.urls'`` somewhere in your ``urls.py`` file.
+
+#. Use it as you would any other Django auth backend.
+
+Running Tests
+=============
+
+Before running tests, you should have ``tox`` installed and available in your
+environment:
+
+.. code-block:: bash
+
+    $ pip install tox
+
+.. NOTE::
+
+    You may need to perform both the above operation and the next inside a
+    python virtualenv, or prefix the above command with ``sudo``, depending on
+    your preference.
+
+To execute the full suite of tests maintained within the project, simply run:
+
+.. code-block:: bash
+
+    $ tox
+
+.. NOTE::
+
+    The first time you run ``tox``, it will take additional time to build
+    virtualenvs. You can later use the ``-r`` option with ``tox`` to rebuild
+    your virtualenv in a similar manner.
+
+To run tests for one or more specific test environments (for example, the most
+common configuration of Python 2.7 and PEP-8), list the environments with the
+``-e`` option, separated by spaces:
+
+.. code-block:: bash
+
+    $ tox -e py27,pep8
+
+See ``tox.ini`` for the full list of available test environments.
diff -pruN 2.4.1-2/doc/source/installation.rst 3.5.0-2/doc/source/installation.rst
--- 2.4.1-2/doc/source/installation.rst	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/doc/source/installation.rst	1970-01-01 00:00:00.000000000 +0000
@@ -1,63 +0,0 @@
-===============
-Getting Started
-===============
-
-Installation
-============
-
-Installing is quick and easy:
-
-#. Run ``pip install django_openstack_auth``.
-
-#. Add ``openstack_auth`` to ``settings.INSTALLED_APPS``.
-
-#. Add ``'openstack_auth.backend.KeystoneBackend'`` to your
-   ``settings.AUTHENTICATION_BACKENDS``, e.g.::
-
-        AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',)
-
-#. Configure your API endpoint(s) in ``settings.py``::
-
-        OPENSTACK_KEYSTONE_URL = "http://example.com:5000/v3"
-
-#. Include ``'openstack_auth.urls'`` somewhere in your ``urls.py`` file.
-
-#. Use it as you would any other Django auth backend.
-
-Running Tests
-=============
-
-Before running tests, you should have ``tox`` installed and available in your
-environment:
-
-.. code-block:: bash
-
-    $ pip install tox
-
-.. NOTE::
-
-    You may need to perform both the above operation and the next inside a
-    python virtualenv, or prefix the above command with ``sudo``, depending on
-    your preference.
-
-To execute the full suite of tests maintained within the project, simply run:
-
-.. code-block:: bash
-
-    $ tox
-
-.. NOTE::
-
-    The first time you run ``tox``, it will take additional time to build
-    virtualenvs. You can later use the ``-r`` option with ``tox`` to rebuild
-    your virtualenv in a similar manner.
-
-To run tests for one or more specific test environments (for example, the most
-common configuration of Python 2.7 and PEP-8), list the environments with the
-``-e`` option, separated by spaces:
-
-.. code-block:: bash
-
-    $ tox -e py27,pep8
-
-See ``tox.ini`` for the full list of available test environments.
diff -pruN 2.4.1-2/doc/source/ref/backend.rst 3.5.0-2/doc/source/ref/backend.rst
--- 2.4.1-2/doc/source/ref/backend.rst	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/doc/source/ref/backend.rst	1970-01-01 00:00:00.000000000 +0000
@@ -1,6 +0,0 @@
-==================
-The Backend Module
-==================
-
-.. automodule:: openstack_auth.backend
-   :members:
diff -pruN 2.4.1-2/doc/source/ref/forms.rst 3.5.0-2/doc/source/ref/forms.rst
--- 2.4.1-2/doc/source/ref/forms.rst	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/doc/source/ref/forms.rst	1970-01-01 00:00:00.000000000 +0000
@@ -1,6 +0,0 @@
-================
-The Forms Module
-================
-
-.. automodule:: openstack_auth.forms
-   :members:
diff -pruN 2.4.1-2/doc/source/ref/user.rst 3.5.0-2/doc/source/ref/user.rst
--- 2.4.1-2/doc/source/ref/user.rst	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/doc/source/ref/user.rst	1970-01-01 00:00:00.000000000 +0000
@@ -1,6 +0,0 @@
-==================
-The ``User`` Class
-==================
-
-.. automodule:: openstack_auth.user
-   :members:
diff -pruN 2.4.1-2/doc/source/ref/utils.rst 3.5.0-2/doc/source/ref/utils.rst
--- 2.4.1-2/doc/source/ref/utils.rst	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/doc/source/ref/utils.rst	1970-01-01 00:00:00.000000000 +0000
@@ -1,6 +0,0 @@
-================
-The Utils Module
-================
-
-.. automodule:: openstack_auth.utils
-   :members:
diff -pruN 2.4.1-2/doc/source/ref/views.rst 3.5.0-2/doc/source/ref/views.rst
--- 2.4.1-2/doc/source/ref/views.rst	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/doc/source/ref/views.rst	1970-01-01 00:00:00.000000000 +0000
@@ -1,6 +0,0 @@
-================
-The Views Module
-================
-
-.. automodule:: openstack_auth.views
-   :members:
diff -pruN 2.4.1-2/doc/source/reference/backend.rst 3.5.0-2/doc/source/reference/backend.rst
--- 2.4.1-2/doc/source/reference/backend.rst	1970-01-01 00:00:00.000000000 +0000
+++ 3.5.0-2/doc/source/reference/backend.rst	2017-07-20 14:16:34.000000000 +0000
@@ -0,0 +1,6 @@
+==================
+The Backend Module
+==================
+
+.. automodule:: openstack_auth.backend
+   :members:
diff -pruN 2.4.1-2/doc/source/reference/forms.rst 3.5.0-2/doc/source/reference/forms.rst
--- 2.4.1-2/doc/source/reference/forms.rst	1970-01-01 00:00:00.000000000 +0000
+++ 3.5.0-2/doc/source/reference/forms.rst	2017-07-20 14:16:34.000000000 +0000
@@ -0,0 +1,6 @@
+================
+The Forms Module
+================
+
+.. automodule:: openstack_auth.forms
+   :members:
diff -pruN 2.4.1-2/doc/source/reference/index.rst 3.5.0-2/doc/source/reference/index.rst
--- 2.4.1-2/doc/source/reference/index.rst	1970-01-01 00:00:00.000000000 +0000
+++ 3.5.0-2/doc/source/reference/index.rst	2017-07-20 14:16:34.000000000 +0000
@@ -0,0 +1,13 @@
+=====================================
+ Django OpenStack Auth API Reference
+=====================================
+
+.. toctree::
+   :maxdepth: 2
+
+   user
+   views
+   forms
+   backend
+   utils
+
diff -pruN 2.4.1-2/doc/source/reference/user.rst 3.5.0-2/doc/source/reference/user.rst
--- 2.4.1-2/doc/source/reference/user.rst	1970-01-01 00:00:00.000000000 +0000
+++ 3.5.0-2/doc/source/reference/user.rst	2017-07-20 14:16:34.000000000 +0000
@@ -0,0 +1,6 @@
+==============
+The User Class
+==============
+
+.. automodule:: openstack_auth.user
+   :members:
diff -pruN 2.4.1-2/doc/source/reference/utils.rst 3.5.0-2/doc/source/reference/utils.rst
--- 2.4.1-2/doc/source/reference/utils.rst	1970-01-01 00:00:00.000000000 +0000
+++ 3.5.0-2/doc/source/reference/utils.rst	2017-07-20 14:16:34.000000000 +0000
@@ -0,0 +1,6 @@
+================
+The Utils Module
+================
+
+.. automodule:: openstack_auth.utils
+   :members:
diff -pruN 2.4.1-2/doc/source/reference/views.rst 3.5.0-2/doc/source/reference/views.rst
--- 2.4.1-2/doc/source/reference/views.rst	1970-01-01 00:00:00.000000000 +0000
+++ 3.5.0-2/doc/source/reference/views.rst	2017-07-20 14:16:34.000000000 +0000
@@ -0,0 +1,6 @@
+================
+The Views Module
+================
+
+.. automodule:: openstack_auth.views
+   :members:
diff -pruN 2.4.1-2/.gitreview 3.5.0-2/.gitreview
--- 2.4.1-2/.gitreview	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/.gitreview	2017-07-20 14:16:34.000000000 +0000
@@ -2,4 +2,3 @@
 host=review.openstack.org
 port=29418
 project=openstack/django_openstack_auth.git
-defaultbranch=stable/newton
diff -pruN 2.4.1-2/openstack_auth/backend.py 3.5.0-2/openstack_auth/backend.py
--- 2.4.1-2/openstack_auth/backend.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/backend.py	2017-07-20 14:16:34.000000000 +0000
@@ -18,9 +18,8 @@ import logging
 import pytz
 
 from django.conf import settings
-from django.utils.module_loading import import_string  # noqa
+from django.utils.module_loading import import_string
 from django.utils.translation import ugettext_lazy as _
-from keystoneauth1 import exceptions as keystone_exceptions
 
 from openstack_auth import exceptions
 from openstack_auth import user as auth_user
@@ -69,8 +68,11 @@ class KeystoneBackend(object):
         If authenticated, this return the user object based on the user ID
         and session data.
 
-        Note: this required monkey-patching the ``contrib.auth`` middleware
-        to make the ``request`` object available to the auth backend class.
+        .. note::
+
+          This required monkey-patching the ``contrib.auth`` middleware
+          to make the ``request`` object available to the auth backend class.
+
         """
         if (hasattr(self, 'request') and
                 user_id == self.request.session["user_id"]):
@@ -110,51 +112,23 @@ class KeystoneBackend(object):
                         'configuration error that should be addressed.')
             raise exceptions.KeystoneAuthException(msg)
 
-        session = utils.get_session()
-        keystone_client_class = utils.get_keystone_client().Client
-
-        try:
-            unscoped_auth_ref = unscoped_auth.get_access(session)
-        except keystone_exceptions.ConnectFailure as exc:
-            LOG.error(str(exc))
-            msg = _('Unable to establish connection to keystone endpoint.')
-            raise exceptions.KeystoneAuthException(msg)
-        except (keystone_exceptions.Unauthorized,
-                keystone_exceptions.Forbidden,
-                keystone_exceptions.NotFound) as exc:
-            LOG.debug(str(exc))
-            raise exceptions.KeystoneAuthException(_('Invalid credentials.'))
-        except (keystone_exceptions.ClientException,
-                keystone_exceptions.AuthorizationFailure) as exc:
-            msg = _("An error occurred authenticating. "
-                    "Please try again later.")
-            LOG.debug(str(exc))
-            raise exceptions.KeystoneAuthException(msg)
+        # the recent project id a user might have set in a cookie
+        recent_project = None
+        request = kwargs.get('request')
+        if request:
+            # Grab recent_project found in the cookie, try to scope
+            # to the last project used.
+            recent_project = request.COOKIES.get('recent_project')
+        unscoped_auth_ref = plugin.get_access_info(unscoped_auth)
 
         # Check expiry for our unscoped auth ref.
         self.check_auth_expiry(unscoped_auth_ref)
 
-        # domain support can require domain scoped tokens to perform
-        # identity operations depending on the policy files being used
-        # for keystone.
-        domain_auth = None
-        domain_auth_ref = None
-        if utils.get_keystone_version() >= 3 and 'user_domain_name' in kwargs:
-            try:
-                token = unscoped_auth_ref.auth_token
-                domain_auth = utils.get_token_auth_plugin(
-                    auth_url,
-                    token,
-                    domain_name=kwargs['user_domain_name'])
-                domain_auth_ref = domain_auth.get_access(session)
-            except Exception:
-                LOG.debug('Error getting domain scoped token.', exc_info=True)
-
-        projects = plugin.list_projects(session,
-                                        unscoped_auth,
-                                        unscoped_auth_ref)
-        # Attempt to scope only to enabled projects
-        projects = [project for project in projects if project.enabled]
+        domain_name = kwargs.get('user_domain_name', None)
+        domain_auth, domain_auth_ref = plugin.get_domain_scoped_auth(
+            unscoped_auth, unscoped_auth_ref, domain_name)
+        scoped_auth, scoped_auth_ref = plugin.get_project_scoped_auth(
+            unscoped_auth, unscoped_auth_ref, recent_project=recent_project)
 
         # Abort if there are no projects for this user and a valid domain
         # token has not been obtained
@@ -171,54 +145,17 @@ class KeystoneBackend(object):
         #                    token)
         #                 3) user cannot obtain a domain scoped token, but can
         #                    obtain a project scoped token
-        if not projects and not domain_auth_ref:
+        if not scoped_auth_ref and domain_auth_ref:
+            # if the user can't obtain a project scoped token, set the scoped
+            # token to be the domain token, if valid
+            scoped_auth = domain_auth
+            scoped_auth_ref = domain_auth_ref
+        elif not scoped_auth_ref and not domain_auth_ref:
             msg = _('You are not authorized for any projects.')
             if utils.get_keystone_version() >= 3:
                 msg = _('You are not authorized for any projects or domains.')
             raise exceptions.KeystoneAuthException(msg)
 
-        # the recent project id a user might have set in a cookie
-        recent_project = None
-        request = kwargs.get('request')
-
-        if request:
-            # Grab recent_project found in the cookie, try to scope
-            # to the last project used.
-            recent_project = request.COOKIES.get('recent_project')
-
-        # if a most recent project was found, try using it first
-        if recent_project:
-            for pos, project in enumerate(projects):
-                if project.id == recent_project:
-                    # move recent project to the beginning
-                    projects.pop(pos)
-                    projects.insert(0, project)
-                    break
-
-        for project in projects:
-            token = unscoped_auth_ref.auth_token
-            scoped_auth = utils.get_token_auth_plugin(auth_url,
-                                                      token=token,
-                                                      project_id=project.id)
-
-            try:
-                scoped_auth_ref = scoped_auth.get_access(session)
-            except (keystone_exceptions.ClientException,
-                    keystone_exceptions.AuthorizationFailure):
-                pass
-            else:
-                break
-        else:
-            # if the user can't obtain a project scoped token, set the scoped
-            # token to be the domain token, if valid
-            if domain_auth_ref:
-                scoped_auth = domain_auth
-                scoped_auth_ref = domain_auth_ref
-            else:
-                # if no domain or project token for user, abort
-                msg = _("Unable to authenticate to any available projects.")
-                raise exceptions.KeystoneAuthException(msg)
-
         # Check expiry for our new scoped token.
         self.check_auth_expiry(scoped_auth_ref)
 
@@ -256,6 +193,9 @@ class KeystoneBackend(object):
             services_region=region_name)
 
         if request is not None:
+            # if no k2k providers exist then the function returns quickly
+            utils.store_initial_k2k_session(auth_url, request, scoped_auth_ref,
+                                            unscoped_auth_ref)
             request.session['unscoped_token'] = unscoped_token
             if domain_auth_ref:
                 # check django session engine, if using cookies, this will not
@@ -276,6 +216,8 @@ class KeystoneBackend(object):
             session_time = min(timeout, int(token_life.total_seconds()))
             request.session.set_expiry(session_time)
 
+            keystone_client_class = utils.get_keystone_client().Client
+            session = utils.get_session()
             scoped_client = keystone_client_class(session=session,
                                                   auth=scoped_auth)
 
diff -pruN 2.4.1-2/openstack_auth/forms.py 3.5.0-2/openstack_auth/forms.py
--- 2.4.1-2/openstack_auth/forms.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/forms.py	2017-07-20 14:16:34.000000000 +0000
@@ -15,11 +15,11 @@ import collections
 import logging
 
 from django.conf import settings
-from django.contrib.auth import authenticate  # noqa
+from django.contrib.auth import authenticate
 from django.contrib.auth import forms as django_auth_forms
 from django import forms
 from django.utils.translation import ugettext_lazy as _
-from django.views.decorators.debug import sensitive_variables  # noqa
+from django.views.decorators.debug import sensitive_variables
 
 from openstack_auth import exceptions
 from openstack_auth import utils
@@ -61,11 +61,22 @@ class Login(django_auth_forms.Authentica
                    'OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT',
                    False):
             last_domain = self.request.COOKIES.get('login_domain', None)
-            self.fields['domain'] = forms.CharField(
-                initial=last_domain,
-                label=_("Domain"),
-                required=True,
-                widget=forms.TextInput(attrs={"autofocus": "autofocus"}))
+            if getattr(settings,
+                       'OPENSTACK_KEYSTONE_DOMAIN_DROPDOWN',
+                       False):
+                self.fields['domain'] = forms.ChoiceField(
+                    label=_("Domain"),
+                    initial=last_domain,
+                    required=True,
+                    choices=getattr(settings,
+                                    'OPENSTACK_KEYSTONE_DOMAIN_CHOICES',
+                                    ()))
+            else:
+                self.fields['domain'] = forms.CharField(
+                    initial=last_domain,
+                    label=_("Domain"),
+                    required=True,
+                    widget=forms.TextInput(attrs={"autofocus": "autofocus"}))
             self.fields['username'].widget = forms.widgets.TextInput()
             fields_ordering = ['domain', 'username', 'password', 'region']
         self.fields['region'].choices = self.get_region_choices()
@@ -124,12 +135,18 @@ class Login(django_auth_forms.Authentica
                                            password=password,
                                            user_domain_name=domain,
                                            auth_url=region)
-            msg = 'Login successful for user "%(username)s".' % \
-                {'username': username}
+            msg = 'Login successful for user "%(username)s", remote address '\
+                '%(remote_ip)s.' % {
+                    'username': username,
+                    'remote_ip': utils.get_client_ip(self.request)
+                }
             LOG.info(msg)
         except exceptions.KeystoneAuthException as exc:
-            msg = 'Login failed for user "%(username)s".' % \
-                {'username': username}
+            msg = 'Login failed for user "%(username)s", remote address '\
+                '%(remote_ip)s.' % {
+                    'username': username,
+                    'remote_ip': utils.get_client_ip(self.request)
+                }
             LOG.warning(msg)
             raise forms.ValidationError(exc)
         if hasattr(self, 'check_for_test_cookie'):  # Dropped in django 1.7
diff -pruN 2.4.1-2/openstack_auth/locale/ar/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/ar/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/ar/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/ar/LC_MESSAGES/django.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,50 +0,0 @@
-# Translations template for django_openstack_auth.
-# Copyright (C) 2015 ORGANIZATION
-# This file is distributed under the same license as the
-# django_openstack_auth project.
-#
-# Translators:
-# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
-msgid ""
-msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
-"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2015-07-16 01:34+0000\n"
-"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
-"Language: ar\n"
-"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
-"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
-"Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
-"Language-Team: Arabic\n"
-
-msgid "An error occurred authenticating. Please try again later."
-msgstr "حدث خطأ أثناء المصادقة , يرجى اعادة المحاولة لاحقا."
-
-msgid "Domain"
-msgstr "نطاق"
-
-msgid "Password"
-msgstr "كلمة المرور"
-
-msgid "Region"
-msgstr "المجال"
-
-msgid "The authentication token issued by the Identity service has expired."
-msgstr "انتهت صلاحية الرمز الموثوق الصادر عن خدمة الهوية"
-
-msgid "Unable to authenticate to any available projects."
-msgstr "لايمكن المصادقة لولوج أيٍّ من المشاريع المتاحة."
-
-msgid "Unable to retrieve authorized projects."
-msgstr "لايمكن اظهار المشاريع المتاحة."
-
-msgid "User Name"
-msgstr "اسم المستخدم"
-
-msgid "You are not authorized for any projects."
-msgstr "لايوجد لديك تصريح لدخول أي مشروع."
diff -pruN 2.4.1-2/openstack_auth/locale/ca/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/ca/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/ca/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/ca/LC_MESSAGES/django.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,51 +0,0 @@
-# Translations template for django_openstack_auth.
-# Copyright (C) 2015 ORGANIZATION
-# This file is distributed under the same license as the
-# django_openstack_auth project.
-#
-# Translators:
-# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
-msgid ""
-msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
-"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2015-07-16 01:34+0000\n"
-"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
-"Language: ca\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
-"Language-Team: Catalan\n"
-
-msgid "An error occurred authenticating. Please try again later."
-msgstr ""
-"S'ha produït un error d'autenticació. Si us plau, prova de nou més tard."
-
-msgid "Domain"
-msgstr "Domini "
-
-msgid "Password"
-msgstr "Contrasenya"
-
-msgid "Region"
-msgstr "Regió"
-
-msgid "The authentication token issued by the Identity service has expired."
-msgstr ""
-"La autentificació ha trobat un problema pel servei d'identitat hagi expirat."
-
-msgid "Unable to authenticate to any available projects."
-msgstr "No es pot autenticar als projectes disponibles."
-
-msgid "Unable to retrieve authorized projects."
-msgstr "No es poden recuperar els projectes autoritzats."
-
-msgid "User Name"
-msgstr "Nom d'usuari"
-
-msgid "You are not authorized for any projects."
-msgstr "No esta autoritzat per a cap projecte."
diff -pruN 2.4.1-2/openstack_auth/locale/cs/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/cs/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/cs/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/cs/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -8,9 +8,9 @@
 # Stanislav Ulrych <stanislav.ulrych@ultimum.io>, 2016. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.3.1.dev3\n"
+"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-06-08 21:45+0000\n"
+"POT-Creation-Date: 2017-01-25 19:41+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -58,9 +58,6 @@ msgstr "Změna projektu \"%(project_name
 msgid "The authentication token issued by the Identity service has expired."
 msgstr "Autentizační token poskytnutý službou identit vypršel."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "Nelze se přihlásit do žádných dostupných projektů."
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "Nelze se připojit ke keystone endpointu."
 
diff -pruN 2.4.1-2/openstack_auth/locale/de/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/de/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/de/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/de/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -8,20 +8,22 @@
 # Robert Simai, 2015
 # Robert Simai, 2015
 # Frank Kloeker <eumel@arcor.de>, 2016. #zanata
+# Andreas Jaeger <jaegerandi@gmail.com>, 2017. #zanata
+# Robert Simai <robert.simai@suse.com>, 2017. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.4.1.dev9\n"
+"Project-Id-Version: django_openstack_auth 3.1.2.dev14\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-09-23 11:12+0000\n"
+"POT-Creation-Date: 2017-04-07 13:52+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2016-03-07 07:54+0000\n"
-"Last-Translator: Andreas Jaeger <jaegerandi@gmail.com>\n"
+"PO-Revision-Date: 2017-03-28 08:35+0000\n"
+"Last-Translator: Robert Simai <robert.simai@suse.com>\n"
 "Language: de\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
+"X-Generator: Zanata 3.9.6\n"
 "Language-Team: German\n"
 
 msgid "An error occurred authenticating. Please try again later."
@@ -32,12 +34,18 @@ msgstr ""
 msgid "Authenticate using"
 msgstr "Authentifizieren mit"
 
+msgid "Could not find service provider ID on Keystone."
+msgstr "Dienstanbieter ID in Keystone nicht gefunden."
+
 msgid "Domain"
 msgstr "Domäne"
 
 msgid "Invalid credentials."
 msgstr "Unzureichende Berechtigung."
 
+msgid "K2K Federation not setup for this session"
+msgstr "K2K Verbund ist für diese Sitzung nicht eingerichtet"
+
 msgid ""
 "No authentication backend could be determined to handle the provided "
 "credentials."
@@ -49,6 +57,10 @@ msgid "Password"
 msgstr "Passwort"
 
 #, python-format
+msgid "Please consider changing your password, it will expire in %s minutes"
+msgstr "Bitte ändern Sie Ihr Passwort. Es läuft in %s Minuten ab."
+
+#, python-format
 msgid "Project switch failed for user \"%(username)s\"."
 msgstr "Projektumschaltung für Benutzer \"%(username)s\" fehlgeschlagen."
 
@@ -56,6 +68,15 @@ msgid "Region"
 msgstr "Region"
 
 #, python-format
+msgid "Service provider authentication failed. %s"
+msgstr "Dienstanbieter Authentifizierung fehlgeschlagen. %s"
+
+#, python-format
+msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
+msgstr ""
+"Umschalten zum Keystone Anbieter \"%(keystone_provider)s\" erfolgreich."
+
+#, python-format
 msgid "Switch to project \"%(project_name)s\" successful."
 msgstr "Umschalten zum Projekt \"%(project_name)s\" erfolgreich."
 
@@ -63,12 +84,12 @@ msgid "The authentication token issued b
 msgstr ""
 "Das vom Identitätsdienst ausgegebene Authentifizierungs-Token ist abgelaufen."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "Außerstande bei irgendeinem verfügbaren Projekt zu authentifizieren."
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "Es kann keine Verbindung zum Keystone Endpunkt aufgebaut werden."
 
+msgid "Unable to retrieve authorized domains."
+msgstr "Die authorisierten Domänen können nicht abgerufen werden."
+
 msgid "Unable to retrieve authorized projects."
 msgstr "Authorisierte Projekte können nicht abgerufen werden."
 
diff -pruN 2.4.1-2/openstack_auth/locale/en_AU/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/en_AU/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/en_AU/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/en_AU/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -9,9 +9,9 @@
 # Tom Fifield <tom@openstack.org>, 2016. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.4.1.dev1\n"
+"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-09-20 22:54+0000\n"
+"POT-Creation-Date: 2017-01-25 19:41+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -59,9 +59,6 @@ msgstr "Switch to project \"%(project_na
 msgid "The authentication token issued by the Identity service has expired."
 msgstr "The authentication token issued by the Identity service has expired."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "Unable to authenticate to any available projects."
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "Unable to establish connection to keystone endpoint."
 
diff -pruN 2.4.1-2/openstack_auth/locale/en_GB/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/en_GB/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/en_GB/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/en_GB/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -9,9 +9,9 @@
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.3.1.dev3\n"
+"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-06-08 21:45+0000\n"
+"POT-Creation-Date: 2017-01-25 19:41+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -59,9 +59,6 @@ msgstr "Switch to project \"%(project_na
 msgid "The authentication token issued by the Identity service has expired."
 msgstr "The authentication token issued by the Identity service has expired."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "Unable to authenticate to any available projects."
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "Unable to establish connection to keystone endpoint."
 
diff -pruN 2.4.1-2/openstack_auth/locale/es/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/es/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/es/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/es/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -12,9 +12,9 @@
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
+"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
+"POT-Creation-Date: 2017-01-25 19:41+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -62,9 +62,6 @@ msgstr "Cambio al proyecto \"%(project_n
 msgid "The authentication token issued by the Identity service has expired."
 msgstr "El token de autenticación emitido por Identity Service ha expirado."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "No se puede autenticar en ninguno de los proyectos disponibles."
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "No ha sido posible establecer conexión con el endpoint de keystone."
 
diff -pruN 2.4.1-2/openstack_auth/locale/es_MX/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/es_MX/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/es_MX/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/es_MX/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -8,9 +8,9 @@
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
+"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
+"POT-Creation-Date: 2017-01-25 19:41+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -61,9 +61,6 @@ msgid "The authentication token issued b
 msgstr ""
 "El token de autenticación otorgado por el servicio de identidad ha expirado."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "No se puede autenticar a los proyectos disponibles."
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "No es posible establecer conexión a punto final de la clave base."
 
diff -pruN 2.4.1-2/openstack_auth/locale/fi_FI/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/fi_FI/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/fi_FI/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/fi_FI/LC_MESSAGES/django.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,50 +0,0 @@
-# Translations template for django_openstack_auth.
-# Copyright (C) 2015 ORGANIZATION
-# This file is distributed under the same license as the
-# django_openstack_auth project.
-#
-# Translators:
-# Toni Willberg <twillber@redhat.com>, 2014
-# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
-msgid ""
-msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
-"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2015-07-16 01:34+0000\n"
-"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
-"Language: fi-FI\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
-"Language-Team: Finnish (Finland)\n"
-
-msgid "An error occurred authenticating. Please try again later."
-msgstr "Tunnistautumisvirhe. Yritä uudelleen myöhemmin."
-
-msgid "Domain"
-msgstr "Toimialue"
-
-msgid "Password"
-msgstr "Salasana"
-
-msgid "Region"
-msgstr "Alue"
-
-msgid "The authentication token issued by the Identity service has expired."
-msgstr "Tunnistuspalvelun myöntämä tunniste on vanhentunut."
-
-msgid "Unable to authenticate to any available projects."
-msgstr "Tunnistautuminen projekteihin epäonnistui."
-
-msgid "Unable to retrieve authorized projects."
-msgstr "Projektilistan nouto ei onnistunut."
-
-msgid "User Name"
-msgstr "Käyttäjätunnus"
-
-msgid "You are not authorized for any projects."
-msgstr "Sinulla ei ole oikeutta yhteenkään projektiin."
diff -pruN 2.4.1-2/openstack_auth/locale/fr/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/fr/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/fr/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/fr/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -10,18 +10,19 @@
 # Maxime COQUEREL <max.coquerel@gmail.com>, 2015
 # Patte D <pattedeph@gmail.com>, 2015
 # Xavier Gauvrit <xavier.gauvrit@cloudwatt.com>, 2014
-# Adrien Cunin <adrien@adriencunin.fr>, 2016. #zanata
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
+# Gérald LONLAS <g.lonlas@gmail.com>, 2016. #zanata
+# JF Taltavull <jftalta@gmail.com>, 2017. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.4.1.dev1\n"
+"Project-Id-Version: django_openstack_auth 3.1.2.dev2\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-09-20 22:54+0000\n"
+"POT-Creation-Date: 2017-02-04 23:47+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2016-08-09 12:52+0000\n"
-"Last-Translator: Adrien Cunin <adrien@adriencunin.fr>\n"
+"PO-Revision-Date: 2017-02-05 10:44+0000\n"
+"Last-Translator: JF Taltavull <jftalta@gmail.com>\n"
 "Language: fr\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 "Generated-By: Babel 2.0\n"
@@ -36,11 +37,17 @@ msgstr ""
 msgid "Authenticate using"
 msgstr "Mode d'authentification"
 
+msgid "Could not find service provider ID on Keystone."
+msgstr "Impossible de trouver l'ID du fournisseur de services sur Keystone."
+
 msgid "Domain"
 msgstr "Domaine"
 
 msgid "Invalid credentials."
-msgstr "Informations d'identification non valides."
+msgstr "Informations d'authentification non valides."
+
+msgid "K2K Federation not setup for this session"
+msgstr "La fédération K2K n'a pas été configurée pour cette session"
 
 msgid ""
 "No authentication backend could be determined to handle the provided "
@@ -58,6 +65,15 @@ msgid "Region"
 msgstr "Région"
 
 #, python-format
+msgid "Service provider authentication failed. %s"
+msgstr "L'authentification du fournisseur de services a échoué. %s"
+
+#, python-format
+msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
+msgstr ""
+"Bascule vers le fournisseur Keystone \"%(keystone_provider)s\" réussie."
+
+#, python-format
 msgid "Switch to project \"%(project_name)s\" successful."
 msgstr "Bascule vers le projet \"%(project_name)s\" réussie. "
 
@@ -65,11 +81,11 @@ msgid "The authentication token issued b
 msgstr ""
 "Le jeton d'authentification délivré par le service d'Identité a expiré."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "Authentification impossible quel que soit le projet."
-
 msgid "Unable to establish connection to keystone endpoint."
-msgstr "Impossible d'établir la connexion avec le point d'accès keystone."
+msgstr "Impossible d'établir la connexion avec le endpoint keystone."
+
+msgid "Unable to retrieve authorized domains."
+msgstr "Impossible de récupérer les domaines autorisés."
 
 msgid "Unable to retrieve authorized projects."
 msgstr "Impossible de récupérer les projets autorisés."
@@ -80,6 +96,5 @@ msgstr "Nom d'utilisateur"
 msgid "You are not authorized for any projects or domains."
 msgstr "Vous n'êtes autorisé sur aucun projet ou domaine."
 
-#, fuzzy
 msgid "You are not authorized for any projects."
 msgstr "Vous n'êtes autorisé sur aucun projet."
diff -pruN 2.4.1-2/openstack_auth/locale/hi/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/hi/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/hi/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/hi/LC_MESSAGES/django.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,49 +0,0 @@
-# Translations template for django_openstack_auth.
-# Copyright (C) 2015 ORGANIZATION
-# This file is distributed under the same license as the
-# django_openstack_auth project.
-#
-# Translators:
-# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
-msgid ""
-msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
-"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2015-07-16 01:34+0000\n"
-"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
-"Language: hi\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
-"Language-Team: Hindi\n"
-
-msgid "An error occurred authenticating. Please try again later."
-msgstr "पहचान सेवा में त्रुटि. बाद में पुन: प्रयास करें."
-
-msgid "Domain"
-msgstr "अनुक्षेत्र"
-
-msgid "Password"
-msgstr "पासवर्ड"
-
-msgid "Region"
-msgstr "क्षेत्र"
-
-msgid "The authentication token issued by the Identity service has expired."
-msgstr "पहचान सेवा द्वारा जारी प्रमाणीकरण टोकन समाप्त हो गया है."
-
-msgid "Unable to authenticate to any available projects."
-msgstr "किसी भी उपलब्ध परियोजनाओं को प्रमाणित करने में असमर्थ."
-
-msgid "Unable to retrieve authorized projects."
-msgstr "अधिकृत परियोजनाओं को पुनः प्राप्त करने में असमर्थ."
-
-msgid "User Name"
-msgstr "उपयोगकर्ता"
-
-msgid "You are not authorized for any projects."
-msgstr "आप किसी भी परियोजनाओं के लिए अधिकृत नहीं हैं."
diff -pruN 2.4.1-2/openstack_auth/locale/id/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/id/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/id/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/id/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -6,20 +6,21 @@
 # Translators:
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
 # suhartono <cloudsuhartono@gmail.com>, 2016. #zanata
+# suhartono <cloudsuhartono@gmail.com>, 2017. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.4.1.dev1\n"
+"Project-Id-Version: django_openstack_auth 3.1.2.dev15\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-09-20 22:54+0000\n"
+"POT-Creation-Date: 2017-04-10 01:40+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2016-09-11 02:55+0000\n"
+"PO-Revision-Date: 2017-04-11 03:47+0000\n"
 "Last-Translator: suhartono <cloudsuhartono@gmail.com>\n"
 "Language: id\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 "Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
+"X-Generator: Zanata 3.9.6\n"
 "Language-Team: Indonesian\n"
 
 msgid "An error occurred authenticating. Please try again later."
@@ -28,12 +29,18 @@ msgstr "Terjadi kesalahan otentikasi. Si
 msgid "Authenticate using"
 msgstr "Otentikasi penggunaan"
 
+msgid "Could not find service provider ID on Keystone."
+msgstr "Tidak dapat menemukan provider layanan ID di Keystone."
+
 msgid "Domain"
 msgstr "Domain"
 
 msgid "Invalid credentials."
 msgstr "Kredensial tidak valid."
 
+msgid "K2K Federation not setup for this session"
+msgstr "K2K Federation tidak menyiapkan untuk sesi ini"
+
 msgid ""
 "No authentication backend could be determined to handle the provided "
 "credentials."
@@ -45,6 +52,11 @@ msgid "Password"
 msgstr "Password"
 
 #, python-format
+msgid "Please consider changing your password, it will expire in %s minutes"
+msgstr ""
+"Silakan mempertimbangkan mengubah sandi Anda, inu akan berakhir pada %s menit"
+
+#, python-format
 msgid "Project switch failed for user \"%(username)s\"."
 msgstr "Switch proyek gagal untuk pengguna \"%(username)s\"."
 
@@ -52,6 +64,14 @@ msgid "Region"
 msgstr "Region"
 
 #, python-format
+msgid "Service provider authentication failed. %s"
+msgstr "Ootentikasi provider layanan gagal. %s"
+
+#, python-format
+msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
+msgstr "Switch ke Keystone Provider \"%(keystone_provider)s\" berhasil."
+
+#, python-format
 msgid "Switch to project \"%(project_name)s\" successful."
 msgstr "Switch proyek \"%(project_name)s\" sukses."
 
@@ -59,12 +79,12 @@ msgid "The authentication token issued b
 msgstr ""
 "Token otentikasi yang dikeluarkan oleh layanan Identity telah berakhir."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "Tidak dapat otentikasi ke proyek-proyek yang tersedia."
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "Tidak dapat melakukan koneksi ke endpoint keystone."
 
+msgid "Unable to retrieve authorized domains."
+msgstr "Tidak dapat mengambil domain yang berwenang."
+
 msgid "Unable to retrieve authorized projects."
 msgstr "Tidak dapat mengambil proyek yang berwenang (authorized)"
 
diff -pruN 2.4.1-2/openstack_auth/locale/it/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/it/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/it/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/it/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -8,9 +8,9 @@
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
+"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
+"POT-Creation-Date: 2017-01-25 19:41+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -60,10 +60,6 @@ msgstr ""
 msgid "The authentication token issued by the Identity service has expired."
 msgstr "Il token di autenticazione che stai usando è scaduto."
 
-msgid "Unable to authenticate to any available projects."
-msgstr ""
-"Impossibile effettuare l'autenticazione su nessun progetto disponibile."
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "Impossibile stabilire una connessione con gli endpoint di keystone. "
 
diff -pruN 2.4.1-2/openstack_auth/locale/ja/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/ja/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/ja/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/ja/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -10,20 +10,22 @@
 # ykatabam <ykatabam@redhat.com>, 2015
 # Mie Yamamoto <myamamot@redhat.com>, 2016. #zanata
 # Yuko Katabami <yukokatabami@gmail.com>, 2016. #zanata
+# Akihiro Motoki <amotoki@gmail.com>, 2017. #zanata
+# Yuko Katabami <yukokatabami@gmail.com>, 2017. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.4.1.dev9\n"
+"Project-Id-Version: django_openstack_auth 3.3.1.dev1\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-09-23 11:12+0000\n"
+"POT-Creation-Date: 2017-07-12 14:10+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2016-09-24 05:51+0000\n"
-"Last-Translator: Mie Yamamoto <myamamot@redhat.com>\n"
+"PO-Revision-Date: 2017-07-11 02:05+0000\n"
+"Last-Translator: Akihiro Motoki <amotoki@gmail.com>\n"
 "Language: ja\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 "Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
+"X-Generator: Zanata 3.9.6\n"
 "Language-Team: Japanese\n"
 
 msgid "An error occurred authenticating. Please try again later."
@@ -32,12 +34,18 @@ msgstr "認証中にエラーが発生
 msgid "Authenticate using"
 msgstr "使用する認証方法"
 
+msgid "Could not find service provider ID on Keystone."
+msgstr "Keystone でサービスプロバイダー ID を見つけることができませんでした。"
+
 msgid "Domain"
 msgstr "ドメイン"
 
 msgid "Invalid credentials."
 msgstr "無効な認証情報です。"
 
+msgid "K2K Federation not setup for this session"
+msgstr "このセッションには K2K フェデレーションは設定されていません。"
+
 msgid ""
 "No authentication backend could be determined to handle the provided "
 "credentials."
@@ -47,6 +55,11 @@ msgid "Password"
 msgstr "パスワード"
 
 #, python-format
+msgid "Please consider changing your password, it will expire in %s minutes"
+msgstr ""
+"パスワードの変更をお薦めします。今のパスワードはあと %s 分で無効になります。"
+
+#, python-format
 msgid "Project switch failed for user \"%(username)s\"."
 msgstr "ユーザー \"%(username)s\" のプロジェクト切り替えに失敗しました。"
 
@@ -54,18 +67,28 @@ msgid "Region"
 msgstr "リージョン"
 
 #, python-format
+msgid "Service provider authentication failed. %s"
+msgstr "サービスプロバイダーの認証に失敗しました。%s"
+
+#, python-format
+msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
+msgstr ""
+"Keystone プロバイダー \"%(keystone_provider)s\" への切り替えが正常に完了しま"
+"した。"
+
+#, python-format
 msgid "Switch to project \"%(project_name)s\" successful."
 msgstr "プロジェクト \"%(project_name)s\" へ正常に切り替えました。"
 
 msgid "The authentication token issued by the Identity service has expired."
 msgstr "Identity サービスにより発行された認証トークンの期限が切れました。"
 
-msgid "Unable to authenticate to any available projects."
-msgstr "利用可能などのプロジェクトに対しても認証できませんでした。"
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "Keystone エンドポイントへの接続を確立できません。"
 
+msgid "Unable to retrieve authorized domains."
+msgstr "認証されたドメインを取得できません。"
+
 msgid "Unable to retrieve authorized projects."
 msgstr "権限を持っているプロジェクトの情報を取得できません。"
 
diff -pruN 2.4.1-2/openstack_auth/locale/ko_KR/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/ko_KR/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/ko_KR/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/ko_KR/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -8,20 +8,22 @@
 # Sungjin Kang <potopro@gmail.com>, 2015
 # Andrea Young Oak Li <youli@redhat.com>, 2016. #zanata
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
+# Jun-Sik Shin <jsshin@smartx.kr>, 2017. #zanata
+# Sungjin Kang <gang.sungjin@gmail.com>, 2017. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.4.1.dev1\n"
+"Project-Id-Version: django_openstack_auth 3.2.1.dev2\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-09-20 22:54+0000\n"
+"POT-Creation-Date: 2017-06-07 17:46+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2016-09-15 11:45+0000\n"
-"Last-Translator: Andrea Young Oak Li <youli@redhat.com>\n"
+"PO-Revision-Date: 2017-06-08 01:53+0000\n"
+"Last-Translator: Jun-Sik Shin <jsshin@smartx.kr>\n"
 "Language: ko-KR\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 "Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
+"X-Generator: Zanata 3.9.6\n"
 "Language-Team: Korean (South Korea)\n"
 
 msgid "An error occurred authenticating. Please try again later."
@@ -30,12 +32,18 @@ msgstr "인증 중 오류가 발생했
 msgid "Authenticate using"
 msgstr "인증 방법"
 
+msgid "Could not find service provider ID on Keystone."
+msgstr "Keystone 에서 서비스 프로바이더 ID 를 찾지 못하였습니다."
+
 msgid "Domain"
 msgstr "도메인"
 
 msgid "Invalid credentials."
 msgstr "올바르지 않은 인증정보입니다."
 
+msgid "K2K Federation not setup for this session"
+msgstr "이 세션에는 K2K Federation 이 설정되지 않았습니다."
+
 msgid ""
 "No authentication backend could be determined to handle the provided "
 "credentials."
@@ -46,6 +54,10 @@ msgid "Password"
 msgstr "암호"
 
 #, python-format
+msgid "Please consider changing your password, it will expire in %s minutes"
+msgstr "암호는 %s 분 후에 만료되므로 변경해 주시기 바랍니다."
+
+#, python-format
 msgid "Project switch failed for user \"%(username)s\"."
 msgstr "사용자 \"%(username)s\" 에 대한 프로젝트 전환에 실패했습니다."
 
@@ -53,18 +65,26 @@ msgid "Region"
 msgstr "지역"
 
 #, python-format
+msgid "Service provider authentication failed. %s"
+msgstr "서비스 프로바이더 인증에 실패했습니다. %s"
+
+#, python-format
+msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
+msgstr "Keystone 프로바이더 \"%(keystone_provider)s\" 로 변경하였습니다."
+
+#, python-format
 msgid "Switch to project \"%(project_name)s\" successful."
 msgstr "프로젝트 \"%(project_name)s\" 로 전환에 성공하였습니다."
 
 msgid "The authentication token issued by the Identity service has expired."
 msgstr "Identity 서비스에서 발급한 인증 토큰이 만료되었습니다."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "사용 가능한 어떤 프로젝트에서도 인증할 수 없습니다."
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "Keystone 엔드포인트에 접속할 수 없습니다."
 
+msgid "Unable to retrieve authorized domains."
+msgstr "인증된 도메인을 찾지 못했습니다."
+
 msgid "Unable to retrieve authorized projects."
 msgstr "인증된 프로젝트를 가져올 수 없습니다."
 
diff -pruN 2.4.1-2/openstack_auth/locale/ne/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/ne/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/ne/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/ne/LC_MESSAGES/django.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,50 +0,0 @@
-# Translations template for django_openstack_auth.
-# Copyright (C) 2015 ORGANIZATION
-# This file is distributed under the same license as the
-# django_openstack_auth project.
-#
-# Translators:
-# Surit Aryal <surit_people@hotmail.com>, 2014
-# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
-msgid ""
-msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
-"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2015-07-16 01:34+0000\n"
-"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
-"Language: ne\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
-"Language-Team: Nepali\n"
-
-msgid "An error occurred authenticating. Please try again later."
-msgstr "प्रमाणीकरणमा त्रुटि भयो। पछि फेरि प्रयास गर्नुहोस्।"
-
-msgid "Domain"
-msgstr "डोमेन"
-
-msgid "Password"
-msgstr "पासवर्ड"
-
-msgid "Region"
-msgstr "क्षेत्र"
-
-msgid "The authentication token issued by the Identity service has expired."
-msgstr "पहिचान सेवा द्वारा जारी प्रमाणीकरण टोकन समाप्त भएको छ।"
-
-msgid "Unable to authenticate to any available projects."
-msgstr "कुनै पनि उपलब्ध परियोजनाहरु प्रमाणीकरण गर्न असमर्थ।"
-
-msgid "Unable to retrieve authorized projects."
-msgstr "अधिकृत परियोजनाहरू प्राप्त गर्न सकिएन।"
-
-msgid "User Name"
-msgstr "प्रयोगकर्ताको नाम"
-
-msgid "You are not authorized for any projects."
-msgstr "तपाईलाई कुनै पनि परियोजनाको अधिकार छैन।"
diff -pruN 2.4.1-2/openstack_auth/locale/nl_NL/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/nl_NL/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/nl_NL/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/nl_NL/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -8,9 +8,9 @@
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
+"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
+"POT-Creation-Date: 2017-01-25 19:41+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -42,9 +42,6 @@ msgid "The authentication token issued b
 msgstr ""
 "De door de identiteitsdienst verstrekte authenticatietoken is verlopen."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "Niet in staat u te authenticeren bij enig beschikbaar project."
-
 msgid "Unable to retrieve authorized projects."
 msgstr "Niet in staat om de geauthentificeerde projecten op te halen."
 
diff -pruN 2.4.1-2/openstack_auth/locale/pa_IN/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/pa_IN/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/pa_IN/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/pa_IN/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -7,9 +7,9 @@
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
+"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
+"POT-Creation-Date: 2017-01-25 19:41+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -55,9 +55,6 @@ msgstr "\"%(project_name)s\" ਪ੍ਰ
 msgid "The authentication token issued by the Identity service has expired."
 msgstr "ਪਛਾਣ ਸੇਵਾ ਵਲੋਂ ਜਾਰੀ ਕੀਤੇ ਪਰਮਾਣਕਿਤਾ ਟੋਕਨ ਦੀ ਮਿਆਦ ਪੁੱਗ ਚੁੱਕੀ ਹੈ।"
 
-msgid "Unable to authenticate to any available projects."
-msgstr "ਕਿਸੇ ਵੀ ਉਪਲੱਬਧ ਪ੍ਰਾਜੈਕਟਾਂ ਨਾਲ ਪ੍ਰਮਾਣਿਕ ਹੋਣ ਤੋਂ ਅਸਮਰੱਥ।"
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "ਕੀਅਸਟੋਨ ਅੰਤ ਬਿੰਦੂ ਨਾਲ ਸੰਬੰਧ ਸਥਾਪਤ ਕਰਨ ਤੋਂ ਅਸਮਰੱਥ।"
 
diff -pruN 2.4.1-2/openstack_auth/locale/pl_PL/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/pl_PL/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/pl_PL/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/pl_PL/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -8,9 +8,9 @@
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
+"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
+"POT-Creation-Date: 2017-01-25 19:41+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
@@ -59,9 +59,6 @@ msgstr "Przełączono na projekt „%(pr
 msgid "The authentication token issued by the Identity service has expired."
 msgstr "Token uwierzytelniania wydany przez usługę uwierzytelniania wygasł."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "Nie można uwierzytelnić do żadnego z istniejących projektów."
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "Nie można nawiązać połączenia z usługą keystone."
 
diff -pruN 2.4.1-2/openstack_auth/locale/pt/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/pt/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/pt/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/pt/LC_MESSAGES/django.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,50 +0,0 @@
-# Translations template for django_openstack_auth.
-# Copyright (C) 2015 ORGANIZATION
-# This file is distributed under the same license as the
-# django_openstack_auth project.
-#
-# Translators:
-# alfalb_mansil, 2014
-# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
-msgid ""
-msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
-"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2015-07-16 01:34+0000\n"
-"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
-"Language: pt\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
-"Language-Team: Portuguese\n"
-
-msgid "An error occurred authenticating. Please try again later."
-msgstr "Ocorreu um erro de autenticação. Por favor, tente de novo mais tarde."
-
-msgid "Domain"
-msgstr "Domínio"
-
-msgid "Password"
-msgstr "Senha"
-
-msgid "Region"
-msgstr "Região"
-
-msgid "The authentication token issued by the Identity service has expired."
-msgstr "A senha de autenticação gerada pelo serviço de identidade expirou."
-
-msgid "Unable to authenticate to any available projects."
-msgstr "Não foi possível autenticar qualquer projeto disponível."
-
-msgid "Unable to retrieve authorized projects."
-msgstr "Não foi possível obter os projetos autorizados."
-
-msgid "User Name"
-msgstr "Nome de Utilizador"
-
-msgid "You are not authorized for any projects."
-msgstr "Não não está autorizado para quaisquer projetos."
diff -pruN 2.4.1-2/openstack_auth/locale/pt_BR/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/pt_BR/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/pt_BR/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/pt_BR/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -7,20 +7,21 @@
 # Andre Campos Bezerra <andrecbezerra@gmail.com>, 2015
 # Gabriel Wainer, 2015
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
+# Fernando Pimenta <fernando.c.pimenta@gmail.com>, 2017. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
+"Project-Id-Version: django_openstack_auth 3.2.1.dev5\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
+"POT-Creation-Date: 2017-06-28 12:08+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2016-03-09 02:54+0000\n"
-"Last-Translator: Marcelo Dieder <marcelodieder@gmail.com>\n"
+"PO-Revision-Date: 2017-06-28 12:37+0000\n"
+"Last-Translator: Fernando Pimenta <fernando.c.pimenta@gmail.com>\n"
 "Language: pt-BR\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 "Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
+"X-Generator: Zanata 3.9.6\n"
 "Language-Team: Portuguese (Brazil)\n"
 
 msgid "An error occurred authenticating. Please try again later."
@@ -29,12 +30,18 @@ msgstr "Um erro ocorreu autenticando. Po
 msgid "Authenticate using"
 msgstr "Autenticar usando"
 
+msgid "Could not find service provider ID on Keystone."
+msgstr "Não consegui encontrar o ID do provedor de serviço no Keystone."
+
 msgid "Domain"
 msgstr "Domínio"
 
 msgid "Invalid credentials."
 msgstr "Credencial Inválida"
 
+msgid "K2K Federation not setup for this session"
+msgstr "K2K Federation não está configurada para esta sessão"
+
 msgid ""
 "No authentication backend could be determined to handle the provided "
 "credentials."
@@ -46,6 +53,11 @@ msgid "Password"
 msgstr "Senha"
 
 #, python-format
+msgid "Please consider changing your password, it will expire in %s minutes"
+msgstr ""
+"Por favor, considere a alteração de sua senha, ela irá expirar em %s minutos"
+
+#, python-format
 msgid "Project switch failed for user \"%(username)s\"."
 msgstr "A troca de projeto falhou para o usuário \"%(username)s\"."
 
@@ -53,18 +65,28 @@ msgid "Region"
 msgstr "Região"
 
 #, python-format
+msgid "Service provider authentication failed. %s"
+msgstr "Autenticação do provedor de serviço falhou. %s"
+
+#, python-format
+msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
+msgstr ""
+"Troca para o Provedor Keystone \"%(keystone_provider)s\" realizada com "
+"sucesso."
+
+#, python-format
 msgid "Switch to project \"%(project_name)s\" successful."
 msgstr "Troca para o projeto \"%(project_name)s\" realizada com sucesso."
 
 msgid "The authentication token issued by the Identity service has expired."
 msgstr "O token de autenticação emitido pelo serviço de identidade expirou."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "Não foi possível autenticar nos projetos disponíveis."
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "Não foi possível estabelecer conexão com o endpoint do Keystone."
 
+msgid "Unable to retrieve authorized domains."
+msgstr "Não foi possível recuperar domínios autorizados."
+
 msgid "Unable to retrieve authorized projects."
 msgstr "Não foi possível obter os projetos autorizados."
 
diff -pruN 2.4.1-2/openstack_auth/locale/ru/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/ru/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/ru/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/ru/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -10,16 +10,17 @@
 # Ilya Shakhat <shakhat@gmail.com>, 2015
 # Nikita Burtsev, 2015
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
+# Artem <amikhalev90@gmail.com>, 2017. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
+"Project-Id-Version: django_openstack_auth 3.1.1.dev1\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
+"POT-Creation-Date: 2017-01-25 19:41+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2015-12-21 09:13+0000\n"
-"Last-Translator: Kirill Zaitsev <k.zaitsev@me.com>\n"
+"PO-Revision-Date: 2017-01-23 12:43+0000\n"
+"Last-Translator: Artem <amikhalev90@gmail.com>\n"
 "Language: ru\n"
 "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
 "%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
@@ -34,6 +35,9 @@ msgstr "Произошла ошибка
 msgid "Authenticate using"
 msgstr "Аутентификация с использованием"
 
+msgid "Could not find service provider ID on Keystone."
+msgstr "Не удалось найти ИД поставщика служб в Keystone."
+
 msgid "Domain"
 msgstr "Домен"
 
@@ -57,15 +61,16 @@ msgid "Region"
 msgstr "Регион"
 
 #, python-format
+msgid "Service provider authentication failed. %s"
+msgstr "Неудачная аутентикация поставщика службы. %s"
+
+#, python-format
 msgid "Switch to project \"%(project_name)s\" successful."
 msgstr "Переключение на проект \"%(project_name)s\" выполнено успешно."
 
 msgid "The authentication token issued by the Identity service has expired."
 msgstr "Время действия токена, выданного сервисом идентификации, истекло."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "Невозможно аутентифицировать ни в одном доступном проекте"
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "Невозможно установить соединение с точкой доступа keystone."
 
diff -pruN 2.4.1-2/openstack_auth/locale/sl_SI/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/sl_SI/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/sl_SI/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/sl_SI/LC_MESSAGES/django.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,50 +0,0 @@
-# Translations template for django_openstack_auth.
-# Copyright (C) 2015 ORGANIZATION
-# This file is distributed under the same license as the
-# django_openstack_auth project.
-#
-# Translators:
-# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
-msgid ""
-msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
-"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2015-07-16 01:34+0000\n"
-"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
-"Language: sl-SI\n"
-"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n"
-"%100==4 ? 2 : 3);\n"
-"Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
-"Language-Team: Slovenian (Slovenia)\n"
-
-msgid "An error occurred authenticating. Please try again later."
-msgstr "Pri avtentikaciji je prišlo do napake. Poskusite znova kasneje."
-
-msgid "Domain"
-msgstr "Domena"
-
-msgid "Password"
-msgstr "Geslo"
-
-msgid "Region"
-msgstr "Regija"
-
-msgid "The authentication token issued by the Identity service has expired."
-msgstr "Dostopovni žeton, ki ga uporablja Keystone, je potekel."
-
-msgid "Unable to authenticate to any available projects."
-msgstr "Prijava v razpoložljive projekte ni uspela."
-
-msgid "Unable to retrieve authorized projects."
-msgstr "Seznama avtoriziranih projektov ni bilo mogoče prikazati."
-
-msgid "User Name"
-msgstr "Uporabniško ime"
-
-msgid "You are not authorized for any projects."
-msgstr "Za nobenega od projektov nimate ustreznih uporabniških pravic."
diff -pruN 2.4.1-2/openstack_auth/locale/sr/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/sr/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/sr/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/sr/LC_MESSAGES/django.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,52 +0,0 @@
-# Translations template for django_openstack_auth.
-# Copyright (C) 2015 ORGANIZATION
-# This file is distributed under the same license as the
-# django_openstack_auth project.
-#
-# Translators:
-# Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
-msgid ""
-msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
-"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2015-07-16 01:34+0000\n"
-"Last-Translator: openstackjenkins <jenkins@openstack.org>\n"
-"Language: sr\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
-"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-"Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
-"Language-Team: Serbian\n"
-
-msgid "An error occurred authenticating. Please try again later."
-msgstr "Greška u proveri autentičnosti. Pokušajte ponovo kasnije."
-
-msgid "Domain"
-msgstr "Domen"
-
-msgid "Password"
-msgstr "Lozinka"
-
-msgid "Region"
-msgstr "Region"
-
-msgid "The authentication token issued by the Identity service has expired."
-msgstr ""
-"Token za dokaz autentičnosti dodeljen od servisa za identifikaciju je "
-"istekao."
-
-msgid "Unable to authenticate to any available projects."
-msgstr "Nema projekata za koje je Vaša autentičnost odgovarajuća."
-
-msgid "Unable to retrieve authorized projects."
-msgstr "Nije moguće dosegnuti autorizovane projekte."
-
-msgid "User Name"
-msgstr "Korisničko ime"
-
-msgid "You are not authorized for any projects."
-msgstr "Vi nemate projekat u kome ste autorizovani."
diff -pruN 2.4.1-2/openstack_auth/locale/tr_TR/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/tr_TR/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/tr_TR/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/tr_TR/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -6,20 +6,21 @@
 # Translators:
 # İşbaran Akçayır <isbaran@gmail.com>, 2015
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
+# işbaran akçayır <isbaran@gmail.com>, 2017. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
+"Project-Id-Version: django_openstack_auth 3.1.2.dev20\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
+"POT-Creation-Date: 2017-05-18 13:23+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2016-03-04 01:43+0000\n"
-"Last-Translator: Mücahit Büyükyılmaz <mucahit@deltanoc.com>\n"
+"PO-Revision-Date: 2017-05-22 09:01+0000\n"
+"Last-Translator: Copied by Zanata <copied-by-zanata@zanata.org>\n"
 "Language: tr-TR\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 "Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
+"X-Generator: Zanata 3.9.6\n"
 "Language-Team: Turkish (Turkey)\n"
 
 msgid "An error occurred authenticating. Please try again later."
@@ -28,12 +29,18 @@ msgstr "Bilinmeyen bir hata oluştu. Lü
 msgid "Authenticate using"
 msgstr "Kimlik doğrulama"
 
+msgid "Could not find service provider ID on Keystone."
+msgstr "Keystone için servis sağlayıcı ID'si bulunamadı."
+
 msgid "Domain"
 msgstr "Domain"
 
 msgid "Invalid credentials."
 msgstr "Geçersiz giriş bilgileri."
 
+msgid "K2K Federation not setup for this session"
+msgstr "K2K Birleşimi bu oturum için kurulmamış"
+
 msgid ""
 "No authentication backend could be determined to handle the provided "
 "credentials."
@@ -45,6 +52,11 @@ msgid "Password"
 msgstr "Şifre"
 
 #, python-format
+msgid "Please consider changing your password, it will expire in %s minutes"
+msgstr ""
+"Lütfen parolanızı değiştirmeyin düşünün, %s dakikada süresini dolduracak"
+
+#, python-format
 msgid "Project switch failed for user \"%(username)s\"."
 msgstr "\"%(username)s\" kullanıcısı için proje geçişi başarısız."
 
@@ -52,6 +64,14 @@ msgid "Region"
 msgstr "Bölge"
 
 #, python-format
+msgid "Service provider authentication failed. %s"
+msgstr "Servis sağlayıcı kimlik doğrulama başarısız. %s"
+
+#, python-format
+msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
+msgstr "\"%(keystone_provider)s\" Keystone Sağlayıcısına geçiş başarılı."
+
+#, python-format
 msgid "Switch to project \"%(project_name)s\" successful."
 msgstr "\"%(project_name)s\" projesine geçiş başarılı."
 
@@ -59,12 +79,12 @@ msgid "The authentication token issued b
 msgstr ""
 "Kimlik servisi tarafından verilen kimlik doğrulama belirtecinin süresi doldu."
 
-msgid "Unable to authenticate to any available projects."
-msgstr "Mevcut projelerde kimlik doğrulanamadı."
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "Keystone uç noktasına bağlantı kurulamadı."
 
+msgid "Unable to retrieve authorized domains."
+msgstr "Yetkili alanlar alınamadı."
+
 msgid "Unable to retrieve authorized projects."
 msgstr "Yetkiniz bulunan projeler alınamadı."
 
diff -pruN 2.4.1-2/openstack_auth/locale/zh_CN/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/zh_CN/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/zh_CN/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/zh_CN/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -10,20 +10,21 @@
 # Ying Chun Guo <daisy.ycguo@gmail.com>, 2015
 # 刘俊朋 <liujunpeng@inspur.com>, 2015
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
+# liujunpeng <liujunpeng@inspur.com>, 2017. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
+"Project-Id-Version: django_openstack_auth 3.1.2.dev10\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
+"POT-Creation-Date: 2017-03-14 12:20+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2016-03-23 06:22+0000\n"
-"Last-Translator: qingszhao <zdqyuqing@163.com>\n"
+"PO-Revision-Date: 2017-02-10 12:14+0000\n"
+"Last-Translator: liujunpeng <liujunpeng@inspur.com>\n"
 "Language: zh-CN\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 "Generated-By: Babel 2.0\n"
-"X-Generator: Zanata 3.7.3\n"
+"X-Generator: Zanata 3.9.6\n"
 "Language-Team: Chinese (China)\n"
 
 msgid "An error occurred authenticating. Please try again later."
@@ -32,12 +33,18 @@ msgstr "认证出现错误。请稍后
 msgid "Authenticate using"
 msgstr "使用认证"
 
+msgid "Could not find service provider ID on Keystone."
+msgstr "在Keystone上不能找到服务提供者ID。"
+
 msgid "Domain"
 msgstr "域"
 
 msgid "Invalid credentials."
 msgstr "凭据无效."
 
+msgid "K2K Federation not setup for this session"
+msgstr "会话没有建立K2K联邦"
+
 msgid ""
 "No authentication backend could be determined to handle the provided "
 "credentials."
@@ -54,18 +61,26 @@ msgid "Region"
 msgstr "区域"
 
 #, python-format
+msgid "Service provider authentication failed. %s"
+msgstr "服务提供者认证失败。%s"
+
+#, python-format
+msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
+msgstr "成功切换到 Keystone 提供者\"%(keystone_provider)s\"."
+
+#, python-format
 msgid "Switch to project \"%(project_name)s\" successful."
 msgstr "成功切换到项目\"%(project_name)s\""
 
 msgid "The authentication token issued by the Identity service has expired."
 msgstr "身份认证令牌已过期。"
 
-msgid "Unable to authenticate to any available projects."
-msgstr "无法验证任何可用的项目。"
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "无法连接keystone端点。"
 
+msgid "Unable to retrieve authorized domains."
+msgstr "无法获取授权域。"
+
 msgid "Unable to retrieve authorized projects."
 msgstr "无法获取授权的项目。"
 
diff -pruN 2.4.1-2/openstack_auth/locale/zh_TW/LC_MESSAGES/django.po 3.5.0-2/openstack_auth/locale/zh_TW/LC_MESSAGES/django.po
--- 2.4.1-2/openstack_auth/locale/zh_TW/LC_MESSAGES/django.po	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/locale/zh_TW/LC_MESSAGES/django.po	2017-07-20 14:16:34.000000000 +0000
@@ -6,16 +6,17 @@
 # Translators:
 # Zhang Xiaowei <zero00072@gmail.com>, 2015
 # Andreas Jaeger <jaegerandi@gmail.com>, 2016. #zanata
+# Ching Kuo <gene@openstack.org>, 2017. #zanata
 msgid ""
 msgstr ""
-"Project-Id-Version: django_openstack_auth 2.2.1.dev12\n"
+"Project-Id-Version: django_openstack_auth 3.1.1.dev12\n"
 "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n"
-"POT-Creation-Date: 2016-05-09 19:52+0000\n"
+"POT-Creation-Date: 2017-02-02 16:33+0000\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"PO-Revision-Date: 2016-03-22 09:32+0000\n"
-"Last-Translator: Jennifer <cristxu@tw.ibm.com>\n"
+"PO-Revision-Date: 2017-02-02 07:53+0000\n"
+"Last-Translator: Ching Kuo <gene@openstack.org>\n"
 "Language: zh-TW\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
 "Generated-By: Babel 2.0\n"
@@ -28,12 +29,18 @@ msgstr "認證發生錯誤。請稍後
 msgid "Authenticate using"
 msgstr "認證使用"
 
+msgid "Could not find service provider ID on Keystone."
+msgstr "在Keystone找不到服務提供者ID"
+
 msgid "Domain"
 msgstr "地域"
 
 msgid "Invalid credentials."
 msgstr "無效的認證。"
 
+msgid "K2K Federation not setup for this session"
+msgstr "這個session並未設定K2K聯合"
+
 msgid ""
 "No authentication backend could be determined to handle the provided "
 "credentials."
@@ -50,18 +57,26 @@ msgid "Region"
 msgstr "區域"
 
 #, python-format
+msgid "Service provider authentication failed. %s"
+msgstr "服務來源認證錯誤。%s"
+
+#, python-format
+msgid "Switch to Keystone Provider \"%(keystone_provider)s\"successful."
+msgstr "成功切換認證來源到 \"%(keystone_provider)s\""
+
+#, python-format
 msgid "Switch to project \"%(project_name)s\" successful."
 msgstr "成功切換至專案 \"%(project_name)s\"。"
 
 msgid "The authentication token issued by the Identity service has expired."
 msgstr "認證伺服器發佈的門票已經過期。"
 
-msgid "Unable to authenticate to any available projects."
-msgstr "無法認證到任何可用的專案。"
-
 msgid "Unable to establish connection to keystone endpoint."
 msgstr "無法建立與 Keystone 端點的連線。"
 
+msgid "Unable to retrieve authorized domains."
+msgstr "無法獲得認證過的地域。"
+
 msgid "Unable to retrieve authorized projects."
 msgstr "無法取回已授權的專案。"
 
diff -pruN 2.4.1-2/openstack_auth/models.py 3.5.0-2/openstack_auth/models.py
--- 2.4.1-2/openstack_auth/models.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/models.py	2017-07-20 14:16:34.000000000 +0000
@@ -12,4 +12,7 @@
 # limitations under the License.
 
 # import the User model in here so Django can find it
-from openstack_auth.user import User        # noqa
+from openstack_auth.user import User
+
+
+__all__ = ['User']
diff -pruN 2.4.1-2/openstack_auth/plugin/base.py 3.5.0-2/openstack_auth/plugin/base.py
--- 2.4.1-2/openstack_auth/plugin/base.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/plugin/base.py	2017-07-20 14:16:34.000000000 +0000
@@ -11,6 +11,7 @@
 # under the License.
 
 import abc
+import logging
 
 from django.utils.translation import ugettext_lazy as _
 from keystoneauth1 import exceptions as keystone_exceptions
@@ -21,6 +22,7 @@ import six
 from openstack_auth import exceptions
 from openstack_auth import utils
 
+LOG = logging.getLogger(__name__)
 __all__ = ['BasePlugin']
 
 
@@ -95,3 +97,145 @@ class BasePlugin(object):
                 keystone_exceptions.AuthorizationFailure):
             msg = _('Unable to retrieve authorized projects.')
             raise exceptions.KeystoneAuthException(msg)
+
+    def list_domains(self, session, auth_plugin, auth_ref=None):
+        try:
+            if self.keystone_version >= 3:
+                client = v3_client.Client(session=session, auth=auth_plugin)
+                return client.auth.domains()
+            else:
+                return []
+        except (keystone_exceptions.ClientException,
+                keystone_exceptions.AuthorizationFailure):
+            msg = _('Unable to retrieve authorized domains.')
+            raise exceptions.KeystoneAuthException(msg)
+
+    def get_access_info(self, keystone_auth):
+        """Get the access info from an unscoped auth
+
+        This function provides the base functionality that the
+        plugins will use to authenticate and get the access info object.
+
+        :param keystone_auth: keystoneauth1 identity plugin
+        :raises: exceptions.KeystoneAuthException on auth failure
+        :returns: keystoneclient.access.AccessInfo
+        """
+        session = utils.get_session()
+
+        try:
+            unscoped_auth_ref = keystone_auth.get_access(session)
+        except keystone_exceptions.ConnectFailure as exc:
+            LOG.error(str(exc))
+            msg = _('Unable to establish connection to keystone endpoint.')
+            raise exceptions.KeystoneAuthException(msg)
+        except (keystone_exceptions.Unauthorized,
+                keystone_exceptions.Forbidden,
+                keystone_exceptions.NotFound) as exc:
+            LOG.debug(str(exc))
+            raise exceptions.KeystoneAuthException(_('Invalid credentials.'))
+        except (keystone_exceptions.ClientException,
+                keystone_exceptions.AuthorizationFailure) as exc:
+            msg = _("An error occurred authenticating. "
+                    "Please try again later.")
+            LOG.debug(str(exc))
+            raise exceptions.KeystoneAuthException(msg)
+        return unscoped_auth_ref
+
+    def get_project_scoped_auth(self, unscoped_auth, unscoped_auth_ref,
+                                recent_project=None):
+        """Get the project scoped keystone auth and access info
+
+        This function returns a project scoped keystone token plugin
+        and AccessInfo object.
+
+        :param unscoped_auth: keystone auth plugin
+        :param unscoped_auth_ref: keystoneclient.access.AccessInfo` or None.
+        :param recent_project: project that we should try to scope to
+        :return: keystone token auth plugin, AccessInfo object
+        """
+        auth_url = unscoped_auth.auth_url
+        session = utils.get_session()
+
+        projects = self.list_projects(
+            session, unscoped_auth, unscoped_auth_ref)
+        # Attempt to scope only to enabled projects
+        projects = [project for project in projects if project.enabled]
+
+        # if a most recent project was found, try using it first
+        if recent_project:
+            for pos, project in enumerate(projects):
+                if project.id == recent_project:
+                    # move recent project to the beginning
+                    projects.pop(pos)
+                    projects.insert(0, project)
+                    break
+
+        scoped_auth = None
+        scoped_auth_ref = None
+        for project in projects:
+            token = unscoped_auth_ref.auth_token
+            scoped_auth = utils.get_token_auth_plugin(auth_url,
+                                                      token=token,
+                                                      project_id=project.id)
+            try:
+                scoped_auth_ref = scoped_auth.get_access(session)
+            except (keystone_exceptions.ClientException,
+                    keystone_exceptions.AuthorizationFailure):
+                LOG.info('Attempted scope to project %s failed, will attempt'
+                         'to scope to another project.' % project.name)
+                pass
+            else:
+                break
+
+        return scoped_auth, scoped_auth_ref
+
+    def get_domain_scoped_auth(self, unscoped_auth, unscoped_auth_ref,
+                               domain_name=None):
+        """Get the domain scoped keystone auth and access info
+
+        This function returns a domain scoped keystone token plugin
+        and AccessInfo object.
+
+        :param unscoped_auth: keystone auth plugin
+        :param unscoped_auth_ref: keystoneclient.access.AccessInfo` or None.
+        :param domain_name: domain that we should try to scope to
+        :return: keystone token auth plugin, AccessInfo object
+        """
+        session = utils.get_session()
+        auth_url = unscoped_auth.auth_url
+
+        if utils.get_keystone_version() < 3:
+            return None, None
+        if domain_name:
+            domains = [domain_name]
+        else:
+            domains = self.list_domains(session,
+                                        unscoped_auth,
+                                        unscoped_auth_ref)
+            domains = [domain.name for domain in domains if domain.enabled]
+
+        # domain support can require domain scoped tokens to perform
+        # identity operations depending on the policy files being used
+        # for keystone.
+        domain_auth = None
+        domain_auth_ref = None
+        for domain_name in domains:
+            token = unscoped_auth_ref.auth_token
+            domain_auth = utils.get_token_auth_plugin(
+                auth_url,
+                token,
+                domain_name=domain_name)
+            try:
+                domain_auth_ref = domain_auth.get_access(session)
+            except (keystone_exceptions.ClientException,
+                    keystone_exceptions.AuthorizationFailure):
+                LOG.info('Attempted scope to domain %s failed, will attempt'
+                         'to scope to another domain.' % domain_name)
+                pass
+            else:
+                if len(domains) > 1:
+                    LOG.info("More than one valid domain found for user %s,"
+                             " scoping to %s" %
+                             (unscoped_auth_ref.user_id, domain_name))
+                break
+        return domain_auth, domain_auth_ref
diff -pruN 2.4.1-2/openstack_auth/plugin/__init__.py 3.5.0-2/openstack_auth/plugin/__init__.py
--- 2.4.1-2/openstack_auth/plugin/__init__.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/plugin/__init__.py	2017-07-20 14:16:34.000000000 +0000
@@ -10,11 +10,13 @@
 # License for the specific language governing permissions and limitations
 # under the License.
 
-from openstack_auth.plugin.base import *  # noqa
-from openstack_auth.plugin.password import *  # noqa
-from openstack_auth.plugin.token import *  # noqa
+from openstack_auth.plugin.base import BasePlugin
+from openstack_auth.plugin.k2k import K2KAuthPlugin
+from openstack_auth.plugin.password import PasswordPlugin
+from openstack_auth.plugin.token import TokenPlugin
 
 
 __all__ = ['BasePlugin',
            'PasswordPlugin',
-           'TokenPlugin']
+           'TokenPlugin',
+           'K2KAuthPlugin']
diff -pruN 2.4.1-2/openstack_auth/plugin/k2k.py 3.5.0-2/openstack_auth/plugin/k2k.py
--- 2.4.1-2/openstack_auth/plugin/k2k.py	1970-01-01 00:00:00.000000000 +0000
+++ 3.5.0-2/openstack_auth/plugin/k2k.py	2017-07-20 14:16:34.000000000 +0000
@@ -0,0 +1,107 @@
+# 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.
+
+import logging
+
+from django.conf import settings
+from django.utils.translation import ugettext_lazy as _
+from keystoneauth1.identity import v3 as v3_auth
+
+from openstack_auth import exceptions
+from openstack_auth.plugin import base
+from openstack_auth import utils
+
+LOG = logging.getLogger(__name__)
+
+__all__ = ['K2KAuthPlugin']
+
+
+class K2KAuthPlugin(base.BasePlugin):
+
+    def get_plugin(self, service_provider=None, auth_url=None, plugins=None,
+                   **kwargs):
+        """Authenticate using keystone to keystone federation.
+
+        This plugin uses other v3 plugins to authenticate a user to a
+        identity provider in order to authenticate the user to a service
+        provider
+
+        :param service_provider: service provider ID
+        :param auth_url: Keystone auth url
+        :param plugins: list of openstack_auth plugins to check
+        :returns Keystone2Keystone keystone auth plugin
+        """
+
+        # Avoid mutable default arg for plugins
+        plugins = plugins or []
+
+        # service_provider being None prevents infinite recursion
+        if utils.get_keystone_version() < 3 or not service_provider:
+            return None
+
+        keystone_idp_id = getattr(settings, 'KEYSTONE_PROVIDER_IDP_ID',
+                                  'localkeystone')
+        if service_provider == keystone_idp_id:
+            return None
+
+        for plugin in plugins:
+            unscoped_idp_auth = plugin.get_plugin(plugins=plugins,
+                                                  auth_url=auth_url, **kwargs)
+            if unscoped_idp_auth:
+                break
+        else:
+            LOG.debug('Could not find base authentication backend for '
+                      'K2K plugin with the provided credentials.')
+            return None
+
+        idp_exception = None
+        scoped_idp_auth = None
+        unscoped_auth_ref = base.BasePlugin.get_access_info(
+            self, unscoped_idp_auth)
+        try:
+            scoped_idp_auth, __ = self.get_project_scoped_auth(
+                unscoped_idp_auth, unscoped_auth_ref)
+        except exceptions.KeystoneAuthException as idp_excp:
+            idp_exception = idp_excp
+
+        if not scoped_idp_auth or idp_exception:
+            msg = 'Identity provider authentication Failed.'
+            raise exceptions.KeystoneAuthException(msg)
+
+        session = utils.get_session()
+
+        if scoped_idp_auth.get_sp_auth_url(session, service_provider) is None:
+            msg = _('Could not find service provider ID on Keystone.')
+            raise exceptions.KeystoneAuthException(msg)
+
+        unscoped_auth = v3_auth.Keystone2Keystone(
+            base_plugin=scoped_idp_auth,
+            service_provider=service_provider)
+        return unscoped_auth
+
+    def get_access_info(self, unscoped_auth):
+        """Get the access info object
+
+        We attempt to get the auth ref. If it fails and if the K2K auth plugin
+        was being used then we will prepend a message saying that the error was
+        on the service provider side.
+        :param: unscoped_auth: Keystone auth plugin for unscoped user
+        :returns: keystoneclient.access.AccessInfo object
+        """
+        try:
+            unscoped_auth_ref = base.BasePlugin.get_access_info(
+                self, unscoped_auth)
+        except exceptions.KeystoneAuthException as excp:
+            msg = _('Service provider authentication failed. %s')
+            raise exceptions.KeystoneAuthException(msg % str(excp))
+        return unscoped_auth_ref
diff -pruN 2.4.1-2/openstack_auth/policy.py 3.5.0-2/openstack_auth/policy.py
--- 2.4.1-2/openstack_auth/policy.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/policy.py	2017-07-20 14:16:34.000000000 +0000
@@ -75,7 +75,8 @@ def check(actions, request, target=None)
     to policy setting.
 
     :param actions: list of scope and action to do policy checks on,
-        the composition of which is (scope, action)
+        the composition of which is (scope, action). Multiple actions
+        are treated as a logical AND.
 
         * scope: service type managing the policy for action
 
@@ -153,12 +154,16 @@ def check(actions, request, target=None)
             # needed when a domain scoped token is present
             if scope == 'identity' and domain_credentials:
                 # use domain credentials
-                return _check_credentials(
-                    enforcer[scope], action, target, domain_credentials)
+                if not _check_credentials(enforcer[scope],
+                                          action,
+                                          target,
+                                          domain_credentials):
+                    return False
 
             # use project credentials
-            return _check_credentials(
-                enforcer[scope], action, target, credentials)
+            if not _check_credentials(enforcer[scope],
+                                      action, target, credentials):
+                return False
 
         # if no policy for scope, allow action, underlying API will
         # ultimately block the action if not permitted, treat as though
diff -pruN 2.4.1-2/openstack_auth/tests/data_v3.py 3.5.0-2/openstack_auth/tests/data_v3.py
--- 2.4.1-2/openstack_auth/tests/data_v3.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/tests/data_v3.py	2017-07-20 14:16:34.000000000 +0000
@@ -55,7 +55,8 @@ class TestResponse(requests.Response):
         return self._text
 
 
-def generate_test_data(pki=False):
+def generate_test_data(pki=False, service_providers=False,
+                       endpoint='localhost'):
     '''Builds a set of test_data data as returned by Keystone V2.'''
     test_data = TestDataContainer()
 
@@ -64,19 +65,19 @@ def generate_test_data(pki=False):
         'id': uuid.uuid4().hex,
         'endpoints': [
             {
-                'url': 'http://admin.localhost:35357/v3',
+                'url': 'http://admin.%s:35357/v3' % endpoint,
                 'region': 'RegionOne',
                 'interface': 'admin',
                 'id': uuid.uuid4().hex,
             },
             {
-                'url': 'http://internal.localhost:5000/v3',
+                'url': 'http://internal.%s:5000/v3' % endpoint,
                 'region': 'RegionOne',
                 'interface': 'internal',
                 'id': uuid.uuid4().hex
             },
             {
-                'url': 'http://public.localhost:5000/v3',
+                'url': 'http://public.%s:5000/v3' % endpoint,
                 'region': 'RegionOne',
                 'interface': 'public',
                 'id': uuid.uuid4().hex
@@ -131,43 +132,43 @@ def generate_test_data(pki=False):
         'id': uuid.uuid4().hex,
         'endpoints': [
             {
-                'url': ('http://nova-admin.localhost:8774/v2.0/%s'
-                        % (project_dict_1['id'])),
+                'url': ('http://nova-admin.%s:8774/v2.0/%s'
+                        % (endpoint, project_dict_1['id'])),
                 'region': 'RegionOne',
                 'interface': 'admin',
                 'id': uuid.uuid4().hex,
             },
             {
-                'url': ('http://nova-internal.localhost:8774/v2.0/%s'
-                        % (project_dict_1['id'])),
+                'url': ('http://nova-internal.%s:8774/v2.0/%s'
+                        % (endpoint, project_dict_1['id'])),
                 'region': 'RegionOne',
                 'interface': 'internal',
                 'id': uuid.uuid4().hex
             },
             {
-                'url': ('http://nova-public.localhost:8774/v2.0/%s'
-                        % (project_dict_1['id'])),
+                'url': ('http://nova-public.%s:8774/v2.0/%s'
+                        % (endpoint, project_dict_1['id'])),
                 'region': 'RegionOne',
                 'interface': 'public',
                 'id': uuid.uuid4().hex
             },
             {
-                'url': ('http://nova2-admin.localhost:8774/v2.0/%s'
-                        % (project_dict_1['id'])),
+                'url': ('http://nova2-admin.%s:8774/v2.0/%s'
+                        % (endpoint, project_dict_1['id'])),
                 'region': 'RegionTwo',
                 'interface': 'admin',
                 'id': uuid.uuid4().hex,
             },
             {
-                'url': ('http://nova2-internal.localhost:8774/v2.0/%s'
-                        % (project_dict_1['id'])),
+                'url': ('http://nova2-internal.%s:8774/v2.0/%s'
+                        % (endpoint, project_dict_1['id'])),
                 'region': 'RegionTwo',
                 'interface': 'internal',
                 'id': uuid.uuid4().hex
             },
             {
-                'url': ('http://nova2-public.localhost:8774/v2.0/%s'
-                        % (project_dict_1['id'])),
+                'url': ('http://nova2-public.%s:8774/v2.0/%s'
+                        % (endpoint, project_dict_1['id'])),
                 'region': 'RegionTwo',
                 'interface': 'public',
                 'id': uuid.uuid4().hex
@@ -218,6 +219,19 @@ def generate_test_data(pki=False):
         }
     }
 
+    sp_list = None
+    if service_providers:
+        test_data.sp_auth_url = 'http://service_provider_endp:5000/v3'
+        test_data.service_provider_id = 'k2kserviceprovider'
+        # The access info for the identity provider
+        # should return a list of service providers
+        sp_list = [
+            {'auth_url': test_data.sp_auth_url,
+             'id': test_data.service_provider_id,
+             'sp_url': 'https://k2kserviceprovider/sp_url'}
+        ]
+        scoped_token_dict['token']['service_providers'] = sp_list
+
     test_data.scoped_access_info = access.create(
         resp=auth_response,
         body=scoped_token_dict
@@ -264,6 +278,9 @@ def generate_test_data(pki=False):
         }
     }
 
+    if service_providers:
+        unscoped_token_dict['token']['service_providers'] = sp_list
+
     test_data.unscoped_access_info = access.create(
         resp=auth_response,
         body=unscoped_token_dict
diff -pruN 2.4.1-2/openstack_auth/tests/settings.py 3.5.0-2/openstack_auth/tests/settings.py
--- 2.4.1-2/openstack_auth/tests/settings.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/tests/settings.py	2017-07-20 14:16:34.000000000 +0000
@@ -13,6 +13,8 @@
 
 import os
 
+ALLOWED_HOSTS = ['*']
+
 DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3'}}
 
 INSTALLED_APPS = [
@@ -70,3 +72,5 @@ TEMPLATES = [
         'APP_DIRS': True,
     },
 ]
+
+AUTH_USER_MODEL = 'openstack_auth.User'
diff -pruN 2.4.1-2/openstack_auth/tests/tests.py 3.5.0-2/openstack_auth/tests/tests.py
--- 2.4.1-2/openstack_auth/tests/tests.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/tests/tests.py	2017-07-20 14:16:34.000000000 +0000
@@ -29,7 +29,7 @@ from keystoneclient.v2_0 import client a
 from keystoneclient.v3 import client as client_v3
 import mock
 from mox3 import mox
-from testscenarios import load_tests_apply_scenarios  # noqa
+from testscenarios import load_tests_apply_scenarios
 
 from openstack_auth import policy
 from openstack_auth.tests import data_v2
@@ -55,6 +55,7 @@ class OpenStackAuthTestsMixin(object):
         plugin = self._create_password_auth()
         plugin.get_access(mox.IsA(session.Session)). \
             AndReturn(self.data.unscoped_access_info)
+        plugin.auth_url = settings.OPENSTACK_KEYSTONE_URL
         return self.ks_client_module.Client(session=mox.IsA(session.Session),
                                             auth=plugin)
 
@@ -75,15 +76,18 @@ class OpenStackAuthTestsMixin(object):
         plugin.get_access(mox.IsA(session.Session)).AndRaise(exc)
 
     def _mock_scoped_client_for_tenant(self, auth_ref, tenant_id, url=None,
-                                       client=True):
+                                       client=True, token=None):
         if url is None:
             url = settings.OPENSTACK_KEYSTONE_URL
 
+        if not token:
+            token = self.data.unscoped_access_info.auth_token
+
         plugin = self._create_token_auth(
             tenant_id,
-            token=self.data.unscoped_access_info.auth_token,
+            token=token,
             url=url)
-
+        self.scoped_token_auth = plugin
         plugin.get_access(mox.IsA(session.Session)).AndReturn(auth_ref)
         if client:
             return self.ks_client_module.Client(
@@ -97,6 +101,53 @@ class OpenStackAuthTestsMixin(object):
                 'username': user.name}
 
 
+class OpenStackAuthFederatedTestsMixin(object):
+    """Common functions for federation"""
+    def _mock_unscoped_federated_list_projects(self, client, projects):
+        client.federation = self.mox.CreateMockAnything()
+        client.federation.projects = self.mox.CreateMockAnything()
+        client.federation.projects.list().AndReturn(projects)
+
+    def _mock_unscoped_list_domains(self, client, domains):
+        client.auth = self.mox.CreateMockAnything()
+        client.auth.domains().AndReturn(domains)
+
+    def _mock_unscoped_token_client(self, unscoped, auth_url=None,
+                                    client=True, plugin=None):
+        if not auth_url:
+            auth_url = settings.OPENSTACK_KEYSTONE_URL
+        if unscoped and not plugin:
+            plugin = self._create_token_auth(
+                None,
+                token=unscoped.auth_token,
+                url=auth_url)
+            plugin.get_access(mox.IsA(session.Session)).AndReturn(unscoped)
+        plugin.auth_url = auth_url
+        if client:
+            return self.ks_client_module.Client(
+                session=mox.IsA(session.Session),
+                auth=plugin)
+
+    def _mock_plugin(self, unscoped, auth_url=None):
+        if not auth_url:
+            auth_url = settings.OPENSTACK_KEYSTONE_URL
+        plugin = self._create_token_auth(
+            None,
+            token=unscoped.auth_token,
+            url=auth_url)
+        plugin.get_access(mox.IsA(session.Session)).AndReturn(unscoped)
+        plugin.auth_url = settings.OPENSTACK_KEYSTONE_URL
+        return plugin
+
+    def _mock_federated_client_list_projects(self, unscoped_auth, projects):
+        client = self._mock_unscoped_token_client(None, plugin=unscoped_auth)
+        self._mock_unscoped_federated_list_projects(client, projects)
+
+    def _mock_federated_client_list_domains(self, unscoped_auth, domains):
+        client = self._mock_unscoped_token_client(None, plugin=unscoped_auth)
+        self._mock_unscoped_list_domains(client, domains)
+
+
 class OpenStackAuthTestsV2(OpenStackAuthTestsMixin, test.TestCase):
 
     def setUp(self):
@@ -130,20 +181,6 @@ class OpenStackAuthTestsV2(OpenStackAuth
         client = self._mock_unscoped_client(user)
         self._mock_unscoped_list_tenants(client, tenants)
 
-    def _mock_client_delete_token(self, user, token, url=None):
-        if not url:
-            url = settings.OPENSTACK_KEYSTONE_URL
-
-        plugin = token_endpoint.Token(
-            endpoint=url,
-            token=self.data.unscoped_access_info.auth_token)
-
-        client = self.ks_client_module.Client(session=mox.IsA(session.Session),
-                                              auth=plugin)
-        client.tokens = self.mox.CreateMockAnything()
-        client.tokens.delete(token=token)
-        return client
-
     def _create_password_auth(self, username=None, password=None, url=None):
         if not username:
             username = self.data.user.name
@@ -340,7 +377,6 @@ class OpenStackAuthTestsV2(OpenStackAuth
 
         self._mock_unscoped_client_list_tenants(user, tenants)
         self._mock_scoped_client_for_tenant(unscoped, self.data.tenant_one.id)
-        self._mock_client_delete_token(user, unscoped.auth_token, endpoint)
         self._mock_scoped_client_for_tenant(scoped, tenant.id, url=endpoint,
                                             client=False)
 
@@ -445,7 +481,9 @@ class OpenStackAuthTestsV2(OpenStackAuth
         self.assertEqual(tenant_list, expected_tenants)
 
 
-class OpenStackAuthTestsV3(OpenStackAuthTestsMixin, test.TestCase):
+class OpenStackAuthTestsV3(OpenStackAuthTestsMixin,
+                           OpenStackAuthFederatedTestsMixin,
+                           test.TestCase):
 
     def _mock_unscoped_client_list_projects(self, user, projects):
         client = self._mock_unscoped_client(user)
@@ -460,6 +498,13 @@ class OpenStackAuthTestsV3(OpenStackAuth
         self._mock_unscoped_list_projects_fail(client, user)
 
     def _mock_unscoped_list_projects_fail(self, client, user):
+        plugin = self._create_token_auth(
+            project_id=None,
+            domain_name=DEFAULT_DOMAIN,
+            token=self.data.unscoped_access_info.auth_token,
+            url=settings.OPENSTACK_KEYSTONE_URL)
+        plugin.get_access(mox.IsA(session.Session)).AndReturn(
+            self.data.domain_scoped_access_info)
         client.projects = self.mox.CreateMockAnything()
         client.projects.list(user=user.id).AndRaise(
             keystone_exceptions.AuthorizationFailure)
@@ -546,6 +591,7 @@ class OpenStackAuthTestsV3(OpenStackAuth
         self.mox.StubOutClassWithMocks(v3_auth, 'Token')
         self.mox.StubOutClassWithMocks(v3_auth, 'Password')
         self.mox.StubOutClassWithMocks(client_v3, 'Client')
+        self.mox.StubOutClassWithMocks(v3_auth, 'Keystone2Keystone')
 
     def test_login(self):
         projects = [self.data.project_one, self.data.project_two]
@@ -788,6 +834,247 @@ class OpenStackAuthTestsV3(OpenStackAuth
     def test_switch_region_with_next(self, next=None):
         self.test_switch_region(next='/next_url')
 
+    def test_switch_keystone_provider_remote_fail(self):
+        auth_url = settings.OPENSTACK_KEYSTONE_URL
+        target_provider = 'k2kserviceprovider'
+        self.data = data_v3.generate_test_data(service_providers=True)
+        self.sp_data = data_v3.generate_test_data(endpoint='http://sp2')
+        projects = [self.data.project_one, self.data.project_two]
+        user = self.data.user
+        unscoped = self.data.unscoped_access_info
+        form_data = self.get_form_data(user)
+
+        # mock authenticate
+        self._mock_unscoped_and_domain_list_projects(user, projects)
+        self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
+
+        # mock switch
+        plugin = v3_auth.Token(auth_url=auth_url,
+                               token=unscoped.auth_token,
+                               project_id=None,
+                               reauthenticate=False)
+        plugin.get_access(mox.IsA(session.Session)
+                          ).AndReturn(self.data.unscoped_access_info)
+        plugin.auth_url = auth_url
+        client = self.ks_client_module.Client(session=mox.IsA(session.Session),
+                                              auth=plugin)
+
+        self._mock_unscoped_list_projects(client, user, projects)
+        plugin = self._create_token_auth(
+            self.data.project_one.id,
+            token=self.data.unscoped_access_info.auth_token,
+            url=settings.OPENSTACK_KEYSTONE_URL)
+        plugin.get_access(mox.IsA(session.Session)).AndReturn(
+            settings.OPENSTACK_KEYSTONE_URL)
+        plugin.get_sp_auth_url(
+            mox.IsA(session.Session), target_provider
+        ).AndReturn('https://k2kserviceprovider/sp_url')
+
+        # let the K2K plugin fail when logging in
+        plugin = v3_auth.Keystone2Keystone(
+            base_plugin=plugin, service_provider=target_provider)
+        plugin.get_access(mox.IsA(session.Session)).AndRaise(
+            keystone_exceptions.AuthorizationFailure)
+        self.mox.ReplayAll()
+
+        # Log in
+        url = reverse('login')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+
+        response = self.client.post(url, form_data)
+        self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
+
+        # Switch
+        url = reverse('switch_keystone_provider', args=[target_provider])
+        form_data['keystone_provider'] = target_provider
+        response = self.client.get(url, form_data, follow=True)
+        self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
+
+        # Assert that provider has not changed because of failure
+        self.assertEqual(self.client.session['keystone_provider_id'],
+                         'localkeystone')
+        # These should never change
+        self.assertEqual(self.client.session['k2k_base_unscoped_token'],
+                         unscoped.auth_token)
+        self.assertEqual(self.client.session['k2k_auth_url'], auth_url)
+
+    def test_switch_keystone_provider_remote(self):
+        auth_url = settings.OPENSTACK_KEYSTONE_URL
+        target_provider = 'k2kserviceprovider'
+        self.data = data_v3.generate_test_data(service_providers=True)
+        self.sp_data = data_v3.generate_test_data(endpoint='http://sp2')
+        projects = [self.data.project_one, self.data.project_two]
+        domains = []
+        user = self.data.user
+        unscoped = self.data.unscoped_access_info
+        form_data = self.get_form_data(user)
+
+        # mock authenticate
+        self._mock_unscoped_and_domain_list_projects(user, projects)
+        self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
+
+        # mock switch
+        plugin = v3_auth.Token(auth_url=auth_url,
+                               token=unscoped.auth_token,
+                               project_id=None,
+                               reauthenticate=False)
+        plugin.get_access(mox.IsA(session.Session)).AndReturn(
+            self.data.unscoped_access_info)
+
+        plugin.auth_url = auth_url
+        client = self.ks_client_module.Client(session=mox.IsA(session.Session),
+                                              auth=plugin)
+
+        self._mock_unscoped_list_projects(client, user, projects)
+        plugin = self._create_token_auth(
+            self.data.project_one.id,
+            token=self.data.unscoped_access_info.auth_token,
+            url=settings.OPENSTACK_KEYSTONE_URL)
+        plugin.get_access(mox.IsA(session.Session)).AndReturn(
+            settings.OPENSTACK_KEYSTONE_URL)
+
+        plugin.get_sp_auth_url(
+            mox.IsA(session.Session), target_provider
+        ).AndReturn('https://k2kserviceprovider/sp_url')
+        plugin = v3_auth.Keystone2Keystone(base_plugin=plugin,
+                                           service_provider=target_provider)
+        plugin.get_access(mox.IsA(session.Session)). \
+            AndReturn(self.sp_data.unscoped_access_info)
+        plugin.auth_url = 'http://service_provider_endp:5000/v3'
+
+        # mock authenticate for service provider
+        sp_projects = [self.sp_data.project_one, self.sp_data.project_two]
+        sp_unscoped = self.sp_data.federated_unscoped_access_info
+        sp_unscoped_auth = self._mock_plugin(sp_unscoped,
+                                             auth_url=plugin.auth_url)
+        client = self._mock_unscoped_token_client(None, plugin.auth_url,
+                                                  plugin=sp_unscoped_auth)
+        self._mock_unscoped_list_domains(client, domains)
+        client = self._mock_unscoped_token_client(None, plugin.auth_url,
+                                                  plugin=sp_unscoped_auth)
+        self._mock_unscoped_federated_list_projects(client, sp_projects)
+        self._mock_scoped_client_for_tenant(sp_unscoped,
+                                            self.sp_data.project_one.id,
+                                            url=plugin.auth_url,
+                                            token=sp_unscoped.auth_token)
+
+        self.mox.ReplayAll()
+
+        # Log in
+        url = reverse('login')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+
+        response = self.client.post(url, form_data)
+        self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
+
+        # Switch
+        url = reverse('switch_keystone_provider', args=[target_provider])
+        form_data['keystone_provider'] = target_provider
+        response = self.client.get(url, form_data, follow=True)
+        self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
+
+        # Assert keystone provider has changed
+        self.assertEqual(self.client.session['keystone_provider_id'],
+                         target_provider)
+        # These should not change
+        self.assertEqual(self.client.session['k2k_base_unscoped_token'],
+                         unscoped.auth_token)
+        self.assertEqual(self.client.session['k2k_auth_url'], auth_url)
+
+    def test_switch_keystone_provider_local(self):
+        auth_url = settings.OPENSTACK_KEYSTONE_URL
+        self.data = data_v3.generate_test_data(service_providers=True)
+        keystone_provider = 'localkeystone'
+        projects = [self.data.project_one, self.data.project_two]
+        domains = []
+        user = self.data.user
+        unscoped = self.data.unscoped_access_info
+        form_data = self.get_form_data(user)
+
+        # mock authenticate
+        self._mock_unscoped_and_domain_list_projects(user, projects)
+        self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
+        self._mock_unscoped_token_client(unscoped,
+                                         auth_url=auth_url,
+                                         client=False)
+        unscoped_auth = self._mock_plugin(unscoped)
+        client = self._mock_unscoped_token_client(None, auth_url=auth_url,
+                                                  plugin=unscoped_auth)
+        self._mock_unscoped_list_domains(client, domains)
+        client = self._mock_unscoped_token_client(None, auth_url=auth_url,
+                                                  plugin=unscoped_auth)
+        self._mock_unscoped_list_projects(client, user, projects)
+        self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
+
+        self.mox.ReplayAll()
+
+        # Log in
+        url = reverse('login')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+
+        response = self.client.post(url, form_data)
+        self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
+
+        # Switch
+        url = reverse('switch_keystone_provider', args=[keystone_provider])
+        form_data['keystone_provider'] = keystone_provider
+        response = self.client.get(url, form_data, follow=True)
+        self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
+
+        # Assert nothing has changed since we are going from local to local
+        self.assertEqual(self.client.session['keystone_provider_id'],
+                         keystone_provider)
+        self.assertEqual(self.client.session['k2k_base_unscoped_token'],
+                         unscoped.auth_token)
+        self.assertEqual(self.client.session['k2k_auth_url'], auth_url)
+
+    def test_switch_keystone_provider_local_fail(self):
+        auth_url = settings.OPENSTACK_KEYSTONE_URL
+        self.data = data_v3.generate_test_data(service_providers=True)
+        keystone_provider = 'localkeystone'
+        projects = [self.data.project_one, self.data.project_two]
+        user = self.data.user
+        unscoped = self.data.unscoped_access_info
+        form_data = self.get_form_data(user)
+
+        # mock authenticate
+        self._mock_unscoped_and_domain_list_projects(user, projects)
+        self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
+
+        # Let using the base token for logging in fail
+        plugin = v3_auth.Token(auth_url=auth_url,
+                               token=unscoped.auth_token,
+                               project_id=None,
+                               reauthenticate=False)
+        plugin.get_access(mox.IsA(session.Session)). \
+            AndRaise(keystone_exceptions.AuthorizationFailure)
+        plugin.auth_url = auth_url
+        self.mox.ReplayAll()
+
+        # Log in
+        url = reverse('login')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+
+        response = self.client.post(url, form_data)
+        self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
+
+        # Switch
+        url = reverse('switch_keystone_provider', args=[keystone_provider])
+        form_data['keystone_provider'] = keystone_provider
+        response = self.client.get(url, form_data, follow=True)
+        self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
+
+        # Assert
+        self.assertEqual(self.client.session['keystone_provider_id'],
+                         keystone_provider)
+        self.assertEqual(self.client.session['k2k_base_unscoped_token'],
+                         unscoped.auth_token)
+        self.assertEqual(self.client.session['k2k_auth_url'], auth_url)
+
     def test_tenant_sorting(self):
         projects = [self.data.project_two, self.data.project_one]
         expected_projects = [self.data.project_one, self.data.project_two]
@@ -804,8 +1091,38 @@ class OpenStackAuthTestsV3(OpenStackAuth
             token=unscoped.auth_token)
         self.assertEqual(project_list, expected_projects)
 
+    def test_login_form_multidomain(self):
+        override = self.settings(OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT=True)
+        override.enable()
+        self.addCleanup(override.disable)
+
+        url = reverse('login')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+        self.assertContains(response, 'id="id_domain"')
+        self.assertContains(response, 'name="domain"')
+
+    def test_login_form_multidomain_dropdown(self):
+        override = self.settings(OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT=True,
+                                 OPENSTACK_KEYSTONE_DOMAIN_DROPDOWN=True,
+                                 OPENSTACK_KEYSTONE_DOMAIN_CHOICES=(
+                                     ('Default', 'Default'),)
+                                 )
+        override.enable()
+        self.addCleanup(override.disable)
+
+        url = reverse('login')
+        response = self.client.get(url)
+        self.assertEqual(response.status_code, 200)
+        self.assertContains(response, 'id="id_domain"')
+        self.assertContains(response, 'name="domain"')
+        self.assertContains(response, 'option value="Default"')
+        settings.OPENSTACK_KEYSTONE_DOMAIN_DROPDOWN = False
+
 
-class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin, test.TestCase):
+class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin,
+                               OpenStackAuthFederatedTestsMixin,
+                               test.TestCase):
 
     def _create_token_auth(self, project_id=None, token=None, url=None):
         if not token:
@@ -819,25 +1136,6 @@ class OpenStackAuthTestsWebSSO(OpenStack
                              project_id=project_id,
                              reauthenticate=False)
 
-    def _mock_unscoped_client(self, unscoped):
-        plugin = self._create_token_auth(
-            None,
-            token=unscoped.auth_token,
-            url=settings.OPENSTACK_KEYSTONE_URL)
-        plugin.get_access(mox.IsA(session.Session)).AndReturn(unscoped)
-
-        return self.ks_client_module.Client(session=mox.IsA(session.Session),
-                                            auth=plugin)
-
-    def _mock_unscoped_federated_list_projects(self, client, projects):
-        client.federation = self.mox.CreateMockAnything()
-        client.federation.projects = self.mox.CreateMockAnything()
-        client.federation.projects.list().AndReturn(projects)
-
-    def _mock_unscoped_client_list_projects(self, unscoped, projects):
-        client = self._mock_unscoped_client(unscoped)
-        self._mock_unscoped_federated_list_projects(client, projects)
-
     def setUp(self):
         super(OpenStackAuthTestsWebSSO, self).setUp()
 
@@ -917,11 +1215,14 @@ class OpenStackAuthTestsWebSSO(OpenStack
 
     def test_websso_login(self):
         projects = [self.data.project_one, self.data.project_two]
+        domains = []
         unscoped = self.data.federated_unscoped_access_info
         token = unscoped.auth_token
+        unscoped_auth = self._mock_plugin(unscoped)
 
         form_data = {'token': token}
-        self._mock_unscoped_client_list_projects(unscoped, projects)
+        self._mock_federated_client_list_domains(unscoped_auth, domains)
+        self._mock_federated_client_list_projects(unscoped_auth, projects)
         self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
 
         self.mox.ReplayAll()
@@ -936,11 +1237,14 @@ class OpenStackAuthTestsWebSSO(OpenStack
         settings.OPENSTACK_KEYSTONE_URL = 'http://auth.openstack.org:5000/v3'
 
         projects = [self.data.project_one, self.data.project_two]
+        domains = []
         unscoped = self.data.federated_unscoped_access_info
         token = unscoped.auth_token
+        unscoped_auth = self._mock_plugin(unscoped)
 
         form_data = {'token': token}
-        self._mock_unscoped_client_list_projects(unscoped, projects)
+        self._mock_federated_client_list_domains(unscoped_auth, domains)
+        self._mock_federated_client_list_projects(unscoped_auth, projects)
         self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
 
         self.mox.ReplayAll()
@@ -959,8 +1263,8 @@ class PolicyLoaderTestCase(test.TestCase
         policy.reset()
         enforcer = policy._get_enforcer()
         self.assertEqual(2, len(enforcer))
-        self.assertTrue('identity' in enforcer)
-        self.assertTrue('compute' in enforcer)
+        self.assertIn('identity', enforcer)
+        self.assertIn('compute', enforcer)
 
     def test_policy_reset(self):
         policy._get_enforcer()
@@ -1178,3 +1482,41 @@ class UserTestCase(test.TestCase):
         self.assertTrue(created_token._is_pki_token(
                         self.data.domain_scoped_access_info.auth_token))
         self.assertFalse(created_token._is_pki_token(None))
+
+
+class BehindProxyTestCase(test.TestCase):
+
+    def setUp(self):
+        self.request = http.HttpRequest()
+
+    def test_without_proxy(self):
+        self.request.META['REMOTE_ADDR'] = '10.111.111.2'
+        from openstack_auth.utils import get_client_ip
+        self.assertEqual('10.111.111.2', get_client_ip(self.request))
+
+    def test_with_proxy_no_settings(self):
+        from openstack_auth.utils import get_client_ip
+        self.request.META['REMOTE_ADDR'] = '10.111.111.2'
+        self.request.META['HTTP_X_REAL_IP'] = '192.168.15.33'
+        self.request.META['HTTP_X_FORWARDED_FOR'] = '172.18.0.2'
+        self.assertEqual('10.111.111.2', get_client_ip(self.request))
+
+    def test_with_settings_without_proxy(self):
+        from openstack_auth.utils import get_client_ip
+        self.request.META['REMOTE_ADDR'] = '10.111.111.2'
+        self.assertEqual('10.111.111.2', get_client_ip(self.request))
+
+    @override_settings(SECURE_PROXY_ADDR_HEADER='HTTP_X_FORWARDED_FOR')
+    def test_with_settings_with_proxy_forwardfor(self):
+        from openstack_auth.utils import get_client_ip
+        self.request.META['REMOTE_ADDR'] = '10.111.111.2'
+        self.request.META['HTTP_X_FORWARDED_FOR'] = '172.18.0.2'
+        self.assertEqual('172.18.0.2', get_client_ip(self.request))
+
+    @override_settings(SECURE_PROXY_ADDR_HEADER='HTTP_X_REAL_IP')
+    def test_with_settings_with_proxy_real_ip(self):
+        from openstack_auth.utils import get_client_ip
+        self.request.META['REMOTE_ADDR'] = '10.111.111.2'
+        self.request.META['HTTP_X_REAL_IP'] = '192.168.15.33'
+        self.request.META['HTTP_X_FORWARDED_FOR'] = '172.18.0.2'
+        self.assertEqual('192.168.15.33', get_client_ip(self.request))
diff -pruN 2.4.1-2/openstack_auth/urls.py 3.5.0-2/openstack_auth/urls.py
--- 2.4.1-2/openstack_auth/urls.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/urls.py	2017-07-20 14:16:34.000000000 +0000
@@ -26,7 +26,10 @@ urlpatterns = [
         name='switch_tenants'),
     url(r'^switch_services_region/(?P<region_name>[^/]+)/$',
         views.switch_region,
-        name='switch_services_region')
+        name='switch_services_region'),
+    url(r'^switch_keystone_provider/(?P<keystone_provider>[^/]+)/$',
+        views.switch_keystone_provider,
+        name='switch_keystone_provider')
 ]
 
 if utils.is_websso_enabled():
diff -pruN 2.4.1-2/openstack_auth/user.py 3.5.0-2/openstack_auth/user.py
--- 2.4.1-2/openstack_auth/user.py	2017-11-02 03:43:03.000000000 +0000
+++ 3.5.0-2/openstack_auth/user.py	2017-07-20 14:16:34.000000000 +0000
@@ -11,6 +11,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import datetime
 import hashlib
 import logging
 
@@ -42,11 +43,15 @@ def set_session_from_user(request, user)
 
 def create_user_from_token(request, token, endpoint, services_region=None):
     # if the region is provided, use that, otherwise use the preferred region
+    default_service_regions = getattr(settings, 'DEFAULT_SERVICE_REGIONS', {})
+    default_service_region = default_service_regions.get(endpoint)
     svc_region = services_region or \
-        utils.default_services_region(token.serviceCatalog, request)
+        utils.default_services_region(token.serviceCatalog, request,
+                                      selected_region=default_service_region)
     return User(id=token.user['id'],
                 token=token,
                 user=token.user['name'],
+                password_expires_at=token.user['password_expires_at'],
                 user_domain_id=token.user_domain_id,
                 # We need to consider already logged-in users with an old
                 # version of Token without user_domain_name.
@@ -76,9 +81,11 @@ class Token(object):
     """
     def __init__(self, auth_ref, unscoped_token=None):
         # User-related attributes
-        user = {}
-        user['id'] = auth_ref.user_id
-        user['name'] = auth_ref.username
+        user = {'id': auth_ref.user_id, 'name': auth_ref.username}
+        data = getattr(auth_ref, '_data', {})
+        expiration_date = data.get('token', {}).get('user', {})\
+            .get('password_expires_at')
+        user['password_expires_at'] = expiration_date
         self.user = user
         self.user_domain_id = auth_ref.user_domain_id
         self.user_domain_name = auth_ref.user_domain_name
@@ -105,6 +112,7 @@ class Token(object):
         project['name'] = auth_ref.project_name
         project['is_admin_project'] = getattr(auth_ref, 'is_admin_project',
                                               False)
+        project['domain_id'] = getattr(auth_ref, 'project_domain_id', None)
         self.project = project
         self.tenant = self.project
 
@@ -194,6 +202,11 @@ class User(models.AbstractBaseUser, mode
 
         Unscoped Keystone token.
 
+    .. attribute:: password_expires_at
+
+        Password expiration date. This attribute could be None when using
+        keystone version < 3.0 or if the feature is not enabled in keystone.
+
     """
 
     keystone_user_id = db_models.CharField(primary_key=True, max_length=255)
@@ -204,8 +217,8 @@ class User(models.AbstractBaseUser, mode
                  authorized_tenants=None, endpoint=None, enabled=False,
                  services_region=None, user_domain_id=None,
                  user_domain_name=None, domain_id=None, domain_name=None,
-                 project_id=None, project_name=None,
-                 is_federated=False, unscoped_token=None, password=None):
+                 project_id=None, project_name=None, is_federated=False,
+                 unscoped_token=None, password=None, password_expires_at=None):
         self.id = id
         self.pk = id
         self.token = token
@@ -227,6 +240,7 @@ class User(models.AbstractBaseUser, mode
         self.enabled = enabled
         self._authorized_tenants = authorized_tenants
         self.is_federated = is_federated
+        self.password_expires_at = password_expires_at
 
         # Unscoped token is used for listing user's project that works
         # for both federated and keystone user.
@@ -248,8 +262,9 @@ class User(models.AbstractBaseUser, mode
     def is_token_expired(self, margin=None):
         """Determine if the token is expired.
 
-        Returns ``True`` if the token is expired, ``False`` if not, and
-        ``None`` if there is no token set.
+        :returns:
+          ``True`` if the token is expired, ``False`` if not, and
+          ``None`` if there is no token set.
 
         :param margin:
            A security time margin in seconds before real expiration.
@@ -276,7 +291,7 @@ class User(models.AbstractBaseUser, mode
         def is_anonymous(self):
             """Return if the user is not authenticated.
 
-            Returns ``True`` if not authenticated,``False`` otherwise.
+            :returns: ``True`` if not authenticated,``False`` otherwise.
             """
             return deprecation.CallableBool(not self.is_authenticated)
     else:
@@ -296,7 +311,7 @@ class User(models.AbstractBaseUser, mode
         def is_anonymous(self, margin=None):
             """Return if the user is not authenticated.
 
-            Returns ``True`` if not authenticated,``False`` otherwise.
+            :returns: ``True`` if not authenticated,``False`` otherwise.
 
             :param margin:
                A security time margin in seconds before end of an eventual
@@ -316,7 +331,7 @@ class User(models.AbstractBaseUser, mode
     def is_superuser(self):
         """Evaluates whether this user has admin privileges.
 
-        Returns ``True`` or ``False``.
+        :returns: ``True`` or ``False``.
         """
         admin_roles = utils.get_admin_roles()
         user_roles = {role['name'].lower() for role in self.roles}
@@ -423,5 +438,17 @@ class User(models.AbstractBaseUser, mode
                     return False
         return True
 
+    def time_until_expiration(self):
+        """Returns the number of remaining days until user's password expires.
+
+        Calculates the number days until the user must change their password,
+        once the password expires the user will not able to log in until an
+        admin changes its password.
+        """
+        if self.password_expires_at is not None:
+            expiration_date = datetime.datetime.strptime(
+                self.password_expires_at, "%Y-%m-%dT%H:%M:%S.%f")
+            return expiration_date - datetime.datetime.now()
+
     class Meta(object):
         app_label = 'openstack_auth'
diff -pruN 2.4.1-2/openstack_auth/utils.py 3.5.0-2/openstack_auth/utils.py
--- 2.4.1-2/openstack_auth/utils.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/utils.py	2017-07-20 14:16:34.000000000 +0000
@@ -17,7 +17,6 @@ import re
 
 from django.conf import settings
 from django.contrib import auth
-from django.contrib.auth import middleware
 from django.contrib.auth import models
 from django.utils import timezone
 from keystoneauth1.identity import v2 as v2_auth
@@ -62,6 +61,10 @@ def get_user(request):
 
 
 def patch_middleware_get_user():
+    # NOTE(adriant): We can't import middleware until our customer user model
+    # is actually registered, otherwise a call to get_user_model within the
+    # middleware module will fail.
+    from django.contrib.auth import middleware
     middleware.get_user = middleware_get_user
     auth.get_user = get_user
 
@@ -144,6 +147,7 @@ def get_keystone_client():
 
 
 def is_token_deletion_disabled():
+    LOG.warning("Deprecated TOKEN_DELETION_DISABLED setting is no longer used")
     return getattr(settings, 'TOKEN_DELETION_DISABLED', False)
 
 
@@ -185,6 +189,7 @@ def get_websso_url(request, auth_url, we
     :type websso_auth: string
 
     Example of horizon WebSSO setting::
+
         WEBSSO_CHOICES = (
             ("credentials", "Keystone Credentials"),
             ("oidc", "OpenID Connect"),
@@ -206,12 +211,14 @@ def get_websso_url(request, auth_url, we
     The value in WEBSSO_IDP_MAPPING is expected to be a tuple formatted as
     (<idp_id>, <protocol_id>). Using the values found, a IdP/protocol
     specific URL will be constructed:
+
         /auth/OS-FEDERATION/identity_providers/<idp_id>
         /protocols/<protocol_id>/websso
 
     If no value is found from the WEBSSO_IDP_MAPPING dictionary, it will
     treat the value as the global WebSSO protocol <protocol_id> and
     construct the WebSSO URL by:
+
         /auth/OS-FEDERATION/websso/<protocol_id>
 
     :returns: Keystone WebSSO endpoint.
@@ -368,7 +375,8 @@ def get_project_list(*args, **kwargs):
     return projects
 
 
-def default_services_region(service_catalog, request=None):
+def default_services_region(service_catalog, request=None,
+                            selected_region=None):
     """Returns the first endpoint region for first non-identity service.
 
     Extracted from the service catalog.
@@ -392,8 +400,7 @@ def default_services_region(service_cata
                 LOG.error('No regions can be found in the service catalog.')
                 return None
 
-        selected_region = None
-        if request:
+        if request and selected_region is None:
             selected_region = request.COOKIES.get('services_region',
                                                   available_regions[0])
         if selected_region not in available_regions:
@@ -435,12 +442,14 @@ def using_cookie_backed_sessions():
 def get_admin_roles():
     """Common function for getting the admin roles from settings
 
-       Returns:
-        Set object including all admin roles.
-        If there is no role, this will return empty.
+    :return:
+      Set object including all admin roles.
+      If there is no role, this will return empty::
+
         {
             "foo", "bar", "admin"
         }
+
     """
     admin_roles = {role.lower() for role
                    in getattr(settings, 'OPENSTACK_KEYSTONE_ADMIN_ROLES',
@@ -453,9 +462,10 @@ def get_role_permission(role):
 
     This format is 'openstack.roles.xxx' and 'xxx' is a real role name.
 
-    Returns:
+    :returns:
         String like "openstack.roles.admin"
         If role is None, this will return None.
+
     """
     return "openstack.roles.%s" % role.lower()
 
@@ -465,13 +475,88 @@ def get_admin_permissions():
 
     This format is 'openstack.roles.xxx' and 'xxx' is a real role name.
 
-    Returns:
-        Set object including all admin permission.
-        If there is no permission, this will return empty.
+    :returns:
+       Set object including all admin permission.
+       If there is no permission, this will return empty::
+
         {
             "openstack.roles.foo",
             "openstack.roles.bar",
             "openstack.roles.admin"
         }
+
     """
     return {get_role_permission(role) for role in get_admin_roles()}
+
+
+def get_client_ip(request):
+    """Return client ip address using SECURE_PROXY_ADDR_HEADER variable.
+
+    If not present or not defined on settings then REMOTE_ADDR is used.
+
+    :param request: Django http request object.
+    :type request: django.http.HttpRequest
+
+    :returns: Possible client ip address
+    :rtype: string
+    """
+    _SECURE_PROXY_ADDR_HEADER = getattr(
+        settings, 'SECURE_PROXY_ADDR_HEADER', False
+    )
+    if _SECURE_PROXY_ADDR_HEADER:
+        return request.META.get(
+            _SECURE_PROXY_ADDR_HEADER,
+            request.META.get('REMOTE_ADDR')
+        )
+    return request.META.get('REMOTE_ADDR')
+
+
+def store_initial_k2k_session(auth_url, request, scoped_auth_ref,
+                              unscoped_auth_ref):
+    """Stores session variables if there are k2k service providers
+
+    This stores variables related to Keystone2Keystone federation. This
+    function gets skipped if there are no Keystone service providers.
+    An unscoped token to the identity provider keystone gets stored
+    so that it can be used to do federated login into the service
+    providers when switching keystone providers.
+    The settings file can be configured to set the display name
+    of the local (identity provider) keystone by setting
+    KEYSTONE_PROVIDER_IDP_NAME. The KEYSTONE_PROVIDER_IDP_ID settings
+    variable is used for comparison against the service providers.
+    It should not conflict with any of the service provider ids.
+
+    :param auth_url: base token auth url
+    :param request: Django http request object
+    :param scoped_auth_ref: Scoped Keystone access info object
+    :param unscoped_auth_ref: Unscoped Keystone access info object
+    """
+    keystone_provider_id = request.session.get('keystone_provider_id', None)
+    if keystone_provider_id:
+        return None
+
+    providers = getattr(scoped_auth_ref, 'service_providers', None)
+    if providers:
+        providers = getattr(providers, '_service_providers', None)
+
+    if providers:
+        keystone_idp_name = getattr(settings, 'KEYSTONE_PROVIDER_IDP_NAME',
+                                    'Local Keystone')
+        keystone_idp_id = getattr(
+            settings, 'KEYSTONE_PROVIDER_IDP_ID', 'localkeystone')
+        keystone_identity_provider = {'name': keystone_idp_name,
+                                      'id': keystone_idp_id}
+        # (edtubill) We will use the IDs as the display names
+        # We may want to be able to set display names in the future.
+        keystone_providers = [
+            {'name': provider_id, 'id': provider_id}
+            for provider_id in providers]
+
+        keystone_providers.append(keystone_identity_provider)
+
+        # We treat the Keystone idp ID as None
+        request.session['keystone_provider_id'] = keystone_idp_id
+        request.session['keystone_providers'] = keystone_providers
+        request.session['k2k_base_unscoped_token'] =\
+            unscoped_auth_ref.auth_token
+        request.session['k2k_auth_url'] = auth_url
diff -pruN 2.4.1-2/openstack_auth/views.py 3.5.0-2/openstack_auth/views.py
--- 2.4.1-2/openstack_auth/views.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/openstack_auth/views.py	2017-07-20 14:16:34.000000000 +0000
@@ -14,7 +14,7 @@ import logging
 
 from django.conf import settings
 from django.contrib import auth
-from django.contrib.auth.decorators import login_required  # noqa
+from django.contrib.auth.decorators import login_required
 from django.contrib.auth import views as django_auth_views
 from django.contrib import messages
 from django import http as django_http
@@ -22,20 +22,21 @@ from django import shortcuts
 from django.utils import functional
 from django.utils import http
 from django.utils.translation import ugettext_lazy as _
-from django.views.decorators.cache import never_cache  # noqa
-from django.views.decorators.csrf import csrf_exempt  # noqa
-from django.views.decorators.csrf import csrf_protect  # noqa
-from django.views.decorators.debug import sensitive_post_parameters  # noqa
+from django.views.decorators.cache import never_cache
+from django.views.decorators.csrf import csrf_exempt
+from django.views.decorators.csrf import csrf_protect
+from django.views.decorators.debug import sensitive_post_parameters
 from keystoneauth1 import exceptions as keystone_exceptions
-from keystoneauth1 import token_endpoint
 import six
 
 from openstack_auth import exceptions
 from openstack_auth import forms
+from openstack_auth import plugin
+
 # This is historic and is added back in to not break older versions of
 # Horizon, fix to Horizon to remove this requirement was committed in
 # Juno
-from openstack_auth.forms import Login  # noqa
+from openstack_auth.forms import Login  # noqa:F401
 from openstack_auth import user as auth_user
 from openstack_auth import utils
 
@@ -119,6 +120,16 @@ def login(request, template_name=None, e
         region_name = regions.get(login_region)
         request.session['region_endpoint'] = region
         request.session['region_name'] = region_name
+        expiration_time = request.user.time_until_expiration()
+        threshold_days = getattr(
+            settings, 'PASSWORD_EXPIRES_WARNING_THRESHOLD_DAYS', -1)
+        if expiration_time is not None and \
+                expiration_time.days <= threshold_days:
+            expiration_time = str(expiration_time).rsplit(':', 1)[0]
+            msg = (_('Please consider changing your password, it will expire'
+                     ' in %s minutes') %
+                   expiration_time).replace(':', ' Hours and ')
+            messages.warning(request, msg)
     return res
 
 
@@ -159,17 +170,6 @@ def logout(request, login_url=None, **kw
     msg = 'Logging out user "%(username)s".' % \
         {'username': request.user.username}
     LOG.info(msg)
-    endpoint = request.session.get('region_endpoint')
-
-    # delete the project scoped token
-    token = request.session.get('token')
-    if token and endpoint:
-        delete_token(endpoint=endpoint, token_id=token.id)
-
-    # delete the domain scoped token if set
-    domain_token = request.session.get('domain_token')
-    if domain_token and endpoint:
-        delete_token(endpoint=endpoint, token_id=domain_token.auth_token)
 
     """ Securely logs a user out. """
     return django_auth_views.logout_then_login(request, login_url=login_url,
@@ -178,24 +178,7 @@ def logout(request, login_url=None, **kw
 
 def delete_token(endpoint, token_id):
     """Delete a token."""
-    if utils.is_token_deletion_disabled():
-        return
-    try:
-        endpoint, __ = utils.fix_auth_url_version_prefix(endpoint)
-
-        session = utils.get_session()
-        auth_plugin = token_endpoint.Token(endpoint=endpoint,
-                                           token=token_id)
-        client = utils.get_keystone_client().Client(session=session,
-                                                    auth=auth_plugin)
-        if utils.get_keystone_version() >= 3:
-            client.tokens.revoke_token(token=token_id)
-        else:
-            client.tokens.delete(token=token_id)
-
-        LOG.info('Deleted token %s' % token_id)
-    except keystone_exceptions.ClientException:
-        LOG.info('Could not delete token')
+    LOG.warning("The delete_token method is deprecated and now does nothing")
 
 
 @login_required
@@ -234,10 +217,6 @@ def switch(request, tenant_id, redirect_
         redirect_to = settings.LOGIN_REDIRECT_URL
 
     if auth_ref:
-        old_endpoint = request.session.get('region_endpoint')
-        old_token = request.session.get('token')
-        if old_token and old_endpoint and old_token.id != auth_ref.auth_token:
-            delete_token(endpoint=old_endpoint, token_id=old_token.id)
         user = auth_user.create_user_from_token(
             request,
             auth_user.Token(auth_ref, unscoped_token=unscoped_token),
@@ -274,3 +253,75 @@ def switch_region(request, region_name,
     utils.set_response_cookie(response, 'services_region',
                               request.session['services_region'])
     return response
+
+
+@login_required
+def switch_keystone_provider(request, keystone_provider=None,
+                             redirect_field_name=auth.REDIRECT_FIELD_NAME):
+    """Switches the user's keystone provider using K2K Federation
+
+    If keystone_provider is given then we switch the user to
+    the keystone provider using K2K federation. Otherwise if keystone_provider
+    is None then we switch the user back to the Identity Provider Keystone
+    which a non federated token auth will be used.
+    """
+    base_token = request.session.get('k2k_base_unscoped_token', None)
+    k2k_auth_url = request.session.get('k2k_auth_url', None)
+    keystone_providers = request.session.get('keystone_providers', None)
+
+    if not base_token or not k2k_auth_url:
+        msg = _('K2K Federation not setup for this session')
+        raise exceptions.KeystoneAuthException(msg)
+
+    redirect_to = request.GET.get(redirect_field_name, '')
+    if not is_safe_url(url=redirect_to, host=request.get_host()):
+        redirect_to = settings.LOGIN_REDIRECT_URL
+
+    unscoped_auth_ref = None
+    keystone_idp_id = getattr(
+        settings, 'KEYSTONE_PROVIDER_IDP_ID', 'localkeystone')
+
+    if keystone_provider == keystone_idp_id:
+        current_plugin = plugin.TokenPlugin()
+        unscoped_auth = current_plugin.get_plugin(auth_url=k2k_auth_url,
+                                                  token=base_token)
+    else:
+        # Switch to service provider using K2K federation
+        plugins = [plugin.TokenPlugin()]
+        current_plugin = plugin.K2KAuthPlugin()
+
+        unscoped_auth = current_plugin.get_plugin(
+            auth_url=k2k_auth_url, service_provider=keystone_provider,
+            plugins=plugins, token=base_token)
+
+    try:
+        # Switch to identity provider using token auth
+        unscoped_auth_ref = current_plugin.get_access_info(unscoped_auth)
+    except exceptions.KeystoneAuthException as exc:
+        msg = 'Switching to Keystone Provider %s has failed. %s' \
+              % (keystone_provider, (six.text_type(exc)))
+        messages.error(request, msg)
+
+    if unscoped_auth_ref:
+        try:
+            request.user = auth.authenticate(
+                request=request, auth_url=unscoped_auth.auth_url,
+                token=unscoped_auth_ref.auth_token)
+        except exceptions.KeystoneAuthException as exc:
+            msg = 'Keystone provider switch failed: %s' % six.text_type(exc)
+            res = django_http.HttpResponseRedirect(settings.LOGIN_URL)
+            res.set_cookie('logout_reason', msg, max_age=10)
+            return res
+        auth.login(request, request.user)
+        auth_user.set_session_from_user(request, request.user)
+        request.session['keystone_provider_id'] = keystone_provider
+        request.session['keystone_providers'] = keystone_providers
+        request.session['k2k_base_unscoped_token'] = base_token
+        request.session['k2k_auth_url'] = k2k_auth_url
+        message = (
+            _('Switch to Keystone Provider "%(keystone_provider)s"'
+              'successful.') % {'keystone_provider': keystone_provider})
+        messages.success(request, message)
+
+    response = shortcuts.redirect(redirect_to)
+    return response
diff -pruN 2.4.1-2/.pc/Add-is_authenticated-and-is_anonymous-properties.patch/openstack_auth/user.py 3.5.0-2/.pc/Add-is_authenticated-and-is_anonymous-properties.patch/openstack_auth/user.py
--- 2.4.1-2/.pc/Add-is_authenticated-and-is_anonymous-properties.patch/openstack_auth/user.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/.pc/Add-is_authenticated-and-is_anonymous-properties.patch/openstack_auth/user.py	1970-01-01 00:00:00.000000000 +0000
@@ -1,410 +0,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.
-
-import hashlib
-import logging
-
-from django.conf import settings
-from django.contrib.auth import models
-from django.db import models as db_models
-from keystoneauth1 import exceptions as keystone_exceptions
-from keystoneclient.common import cms as keystone_cms
-import six
-
-from openstack_auth import utils
-
-
-LOG = logging.getLogger(__name__)
-_TOKEN_HASH_ENABLED = getattr(settings, 'OPENSTACK_TOKEN_HASH_ENABLED', True)
-
-
-def set_session_from_user(request, user):
-    request.session['token'] = user.token
-    request.session['user_id'] = user.id
-    request.session['region_endpoint'] = user.endpoint
-    request.session['services_region'] = user.services_region
-    # Update the user object cached in the request
-    request._cached_user = user
-    request.user = user
-
-
-def create_user_from_token(request, token, endpoint, services_region=None):
-    # if the region is provided, use that, otherwise use the preferred region
-    svc_region = services_region or \
-        utils.default_services_region(token.serviceCatalog, request)
-    return User(id=token.user['id'],
-                token=token,
-                user=token.user['name'],
-                user_domain_id=token.user_domain_id,
-                # We need to consider already logged-in users with an old
-                # version of Token without user_domain_name.
-                user_domain_name=getattr(token, 'user_domain_name', None),
-                project_id=token.project['id'],
-                project_name=token.project['name'],
-                domain_id=token.domain['id'],
-                domain_name=token.domain['name'],
-                enabled=True,
-                service_catalog=token.serviceCatalog,
-                roles=token.roles,
-                endpoint=endpoint,
-                services_region=svc_region,
-                is_federated=getattr(token, 'is_federated', False),
-                unscoped_token=getattr(token, 'unscoped_token',
-                                       request.session.get('unscoped_token')))
-
-
-class Token(object):
-    """Encapsulates the AccessInfo object from keystoneclient.
-
-    Token object provides a consistent interface for accessing the keystone
-    token information and service catalog.
-
-    Added for maintaining backward compatibility with horizon that expects
-    Token object in the user object.
-    """
-    def __init__(self, auth_ref, unscoped_token=None):
-        # User-related attributes
-        user = {}
-        user['id'] = auth_ref.user_id
-        user['name'] = auth_ref.username
-        self.user = user
-        self.user_domain_id = auth_ref.user_domain_id
-        self.user_domain_name = auth_ref.user_domain_name
-
-        # Token-related attributes
-        self.id = auth_ref.auth_token
-        self.unscoped_token = unscoped_token
-        if _TOKEN_HASH_ENABLED and self._is_pki_token(self.id):
-            algorithm = getattr(settings, 'OPENSTACK_TOKEN_HASH_ALGORITHM',
-                                'md5')
-            hasher = hashlib.new(algorithm)
-            hasher.update(self.id.encode('utf-8'))
-            self.id = hasher.hexdigest()
-            # Only hash unscoped token if needed
-            if self._is_pki_token(self.unscoped_token):
-                hasher = hashlib.new(algorithm)
-                hasher.update(self.unscoped_token.encode('utf-8'))
-                self.unscoped_token = hasher.hexdigest()
-        self.expires = auth_ref.expires
-
-        # Project-related attributes
-        project = {}
-        project['id'] = auth_ref.project_id
-        project['name'] = auth_ref.project_name
-        project['is_admin_project'] = getattr(auth_ref, 'is_admin_project',
-                                              False)
-        self.project = project
-        self.tenant = self.project
-
-        # Domain-related attributes
-        domain = {}
-        domain['id'] = auth_ref.domain_id
-        domain['name'] = auth_ref.domain_name
-        self.domain = domain
-
-        # Federation-related attributes
-        self.is_federated = auth_ref.is_federated
-        self.roles = [{'name': role} for role in auth_ref.role_names]
-        self.serviceCatalog = auth_ref.service_catalog.catalog
-
-    def _is_pki_token(self, token):
-        """Determines if this is a pki-based token (pki or pkiz)"""
-        if token is None:
-            return False
-        return (keystone_cms.is_ans1_token(token)
-                or keystone_cms.is_pkiz(token))
-
-
-class User(models.AbstractBaseUser, models.AnonymousUser):
-    """A User class with some extra special sauce for Keystone.
-
-    In addition to the standard Django user attributes, this class also has
-    the following:
-
-    .. attribute:: token
-
-        The Keystone token object associated with the current user/tenant.
-
-        The token object is deprecated, user auth_ref instead.
-
-    .. attribute:: tenant_id
-
-        The id of the Keystone tenant for the current user/token.
-
-        The tenant_id keyword argument is deprecated, use project_id instead.
-
-    .. attribute:: tenant_name
-
-        The name of the Keystone tenant for the current user/token.
-
-        The tenant_name keyword argument is deprecated, use project_name
-        instead.
-
-    .. attribute:: project_id
-
-        The id of the Keystone project for the current user/token.
-
-    .. attribute:: project_name
-
-        The name of the Keystone project for the current user/token.
-
-    .. attribute:: service_catalog
-
-        The ``ServiceCatalog`` data returned by Keystone.
-
-    .. attribute:: roles
-
-        A list of dictionaries containing role names and ids as returned
-        by Keystone.
-
-    .. attribute:: services_region
-
-        A list of non-identity service endpoint regions extracted from the
-        service catalog.
-
-    .. attribute:: user_domain_id
-
-        The domain id of the current user.
-
-    .. attribute:: user_domain_name
-
-        The domain name of the current user.
-
-    .. attribute:: domain_id
-
-        The id of the Keystone domain scoped for the current user/token.
-
-    .. attribute:: is_federated
-
-        Whether user is federated Keystone user. (Boolean)
-
-    .. attribute:: unscoped_token
-
-        Unscoped Keystone token.
-
-    """
-
-    keystone_user_id = db_models.CharField(primary_key=True, max_length=255)
-    USERNAME_FIELD = 'keystone_user_id'
-
-    def __init__(self, id=None, token=None, user=None, tenant_id=None,
-                 service_catalog=None, tenant_name=None, roles=None,
-                 authorized_tenants=None, endpoint=None, enabled=False,
-                 services_region=None, user_domain_id=None,
-                 user_domain_name=None, domain_id=None, domain_name=None,
-                 project_id=None, project_name=None,
-                 is_federated=False, unscoped_token=None, password=None):
-        self.id = id
-        self.pk = id
-        self.token = token
-        self.keystone_user_id = id
-        self.username = user
-        self.user_domain_id = user_domain_id
-        self.user_domain_name = user_domain_name
-        self.domain_id = domain_id
-        self.domain_name = domain_name
-        self.project_id = project_id or tenant_id
-        self.project_name = project_name or tenant_name
-        self.service_catalog = service_catalog
-        self._services_region = (
-            services_region
-            or utils.default_services_region(service_catalog)
-        )
-        self.roles = roles or []
-        self.endpoint = endpoint
-        self.enabled = enabled
-        self._authorized_tenants = authorized_tenants
-        self.is_federated = is_federated
-
-        # Unscoped token is used for listing user's project that works
-        # for both federated and keystone user.
-        self.unscoped_token = unscoped_token
-
-        # List of variables to be deprecated.
-        self.tenant_id = self.project_id
-        self.tenant_name = self.project_name
-
-        # Required by AbstractBaseUser
-        self.password = None
-
-    def __unicode__(self):
-        return self.username
-
-    def __repr__(self):
-        return "<%s: %s>" % (self.__class__.__name__, self.username)
-
-    def is_token_expired(self, margin=None):
-        """Determine if the token is expired.
-
-        Returns ``True`` if the token is expired, ``False`` if not, and
-        ``None`` if there is no token set.
-
-        :param margin:
-           A security time margin in seconds before real expiration.
-           Will return ``True`` if the token expires in less than ``margin``
-           seconds of time.
-           A default margin can be set by the TOKEN_TIMEOUT_MARGIN in the
-           django settings.
-
-        """
-        if self.token is None:
-            return None
-        return not utils.is_token_valid(self.token, margin)
-
-    def is_authenticated(self, margin=None):
-        """Checks for a valid authentication.
-
-        :param margin:
-           A security time margin in seconds before end of authentication.
-           Will return ``False`` if authentication ends in less than ``margin``
-           seconds of time.
-           A default margin can be set by the TOKEN_TIMEOUT_MARGIN in the
-           django settings.
-
-        """
-        return (self.token is not None and
-                utils.is_token_valid(self.token, margin))
-
-    def is_anonymous(self, margin=None):
-        """Return if the user is not authenticated.
-
-        Returns ``True`` if not authenticated,``False`` otherwise.
-
-        :param margin:
-           A security time margin in seconds before end of an eventual
-           authentication.
-           Will return ``True`` even if authenticated but that authentication
-           ends in less than ``margin`` seconds of time.
-           A default margin can be set by the TOKEN_TIMEOUT_MARGIN in the
-           django settings.
-
-        """
-        return not self.is_authenticated(margin)
-
-    @property
-    def is_active(self):
-        return self.enabled
-
-    @property
-    def is_superuser(self):
-        """Evaluates whether this user has admin privileges.
-
-        Returns ``True`` or ``False``.
-        """
-        admin_roles = utils.get_admin_roles()
-        user_roles = {role['name'].lower() for role in self.roles}
-        return not admin_roles.isdisjoint(user_roles)
-
-    @property
-    def authorized_tenants(self):
-        """Returns a memoized list of tenants this user may access."""
-        if self.is_authenticated() and self._authorized_tenants is None:
-            endpoint = self.endpoint
-            try:
-                self._authorized_tenants = utils.get_project_list(
-                    user_id=self.id,
-                    auth_url=endpoint,
-                    token=self.unscoped_token,
-                    is_federated=self.is_federated)
-            except (keystone_exceptions.ClientException,
-                    keystone_exceptions.AuthorizationFailure):
-                LOG.exception('Unable to retrieve project list.')
-        return self._authorized_tenants or []
-
-    @authorized_tenants.setter
-    def authorized_tenants(self, tenant_list):
-        self._authorized_tenants = tenant_list
-
-    @property
-    def services_region(self):
-        return self._services_region
-
-    @services_region.setter
-    def services_region(self, region):
-        self._services_region = region
-
-    @property
-    def available_services_regions(self):
-        """Returns list of unique region name values in service catalog."""
-        regions = []
-        if self.service_catalog:
-            for service in self.service_catalog:
-                service_type = service.get('type')
-                if service_type is None or service_type == 'identity':
-                    continue
-                for endpoint in service.get('endpoints', []):
-                    region = utils.get_endpoint_region(endpoint)
-                    if region not in regions:
-                        regions.append(region)
-        return regions
-
-    def save(*args, **kwargs):
-        # Presume we can't write to Keystone.
-        pass
-
-    def delete(*args, **kwargs):
-        # Presume we can't write to Keystone.
-        pass
-
-    # Check for OR'd permission rules, check that user has one of the
-    # required permission.
-    def has_a_matching_perm(self, perm_list, obj=None):
-        """Returns True if the user has one of the specified permissions.
-
-        If object is passed, it checks if the user has any of the required
-        perms for this object.
-        """
-        # If there are no permissions to check, just return true
-        if not perm_list:
-            return True
-        # Check that user has at least one of the required permissions.
-        for perm in perm_list:
-            if self.has_perm(perm, obj):
-                return True
-        return False
-
-    # Override the default has_perms method. Allowing for more
-    # complex combinations of permissions.  Will check for logical AND of
-    # all top level permissions.  Will use logical OR for all first level
-    # tuples (check that use has one permissions in the tuple)
-    #
-    # Examples:
-    #   Checks for all required permissions
-    #   ('openstack.roles.admin', 'openstack.roles.L3-support')
-    #
-    #   Checks for admin AND (L2 or L3)
-    #   ('openstack.roles.admin', ('openstack.roles.L3-support',
-    #                              'openstack.roles.L2-support'),)
-    def has_perms(self, perm_list, obj=None):
-        """Returns True if the user has all of the specified permissions.
-
-        Tuples in the list will possess the required permissions if
-        the user has a permissions matching one of the elements of
-        that tuple
-        """
-        # If there are no permissions to check, just return true
-        if not perm_list:
-            return True
-        for perm in perm_list:
-            if isinstance(perm, six.string_types):
-                # check that the permission matches
-                if not self.has_perm(perm, obj):
-                    return False
-            else:
-                # check that a permission in the tuple matches
-                if not self.has_a_matching_perm(perm, obj):
-                    return False
-        return True
-
-    class Meta(object):
-        app_label = 'openstack_auth'
diff -pruN 2.4.1-2/.pc/applied-patches 3.5.0-2/.pc/applied-patches
--- 2.4.1-2/.pc/applied-patches	2017-11-02 03:43:03.289041563 +0000
+++ 3.5.0-2/.pc/applied-patches	2017-11-02 03:43:03.477046779 +0000
@@ -1 +0,0 @@
-Add-is_authenticated-and-is_anonymous-properties.patch
diff -pruN 2.4.1-2/README.rst 3.5.0-2/README.rst
--- 2.4.1-2/README.rst	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/README.rst	2017-07-20 14:16:34.000000000 +0000
@@ -1,3 +1,12 @@
+========================
+Team and repository tags
+========================
+
+.. image:: http://governance.openstack.org/badges/django_openstack_auth.svg
+    :target: http://governance.openstack.org/reference/tags/index.html
+
+.. Change things from this point on
+
 =====================
 Django OpenStack Auth
 =====================
diff -pruN 2.4.1-2/requirements.txt 3.5.0-2/requirements.txt
--- 2.4.1-2/requirements.txt	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/requirements.txt	2017-07-20 14:16:34.000000000 +0000
@@ -1,10 +1,10 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-pbr>=1.6 # Apache-2.0
-Django<1.9,>=1.8 # BSD
-oslo.config>=3.14.0 # Apache-2.0
-oslo.policy>=1.9.0 # Apache-2.0
-python-keystoneclient!=2.1.0,>=2.0.0 # Apache-2.0
-keystoneauth1>=2.10.0 # Apache-2.0
+pbr!=2.1.0,>=2.0.0 # Apache-2.0
+Django<2.0,>=1.8 # BSD
+oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
+oslo.policy>=1.23.0 # Apache-2.0
+python-keystoneclient>=3.8.0 # Apache-2.0
+keystoneauth1>=2.21.0 # Apache-2.0
 six>=1.9.0 # MIT
diff -pruN 2.4.1-2/setup.cfg 3.5.0-2/setup.cfg
--- 2.4.1-2/setup.cfg	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/setup.cfg	2017-07-20 14:16:34.000000000 +0000
@@ -21,6 +21,7 @@ classifier =
     Programming Language :: Python :: 2.7
     Programming Language :: Python :: 3
     Programming Language :: Python :: 3.4
+    Programming Language :: Python :: 3.5
     Topic :: Internet :: WWW/HTTP
 
 [files]
@@ -28,7 +29,8 @@ packages =
     openstack_auth
 
 [build_sphinx]
-all_files = 1
+all-files = 1
+warning-is-error = 1
 build-dir = doc/build
 source-dir = doc/source
 
diff -pruN 2.4.1-2/setup.py 3.5.0-2/setup.py
--- 2.4.1-2/setup.py	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/setup.py	2017-07-20 14:16:34.000000000 +0000
@@ -25,5 +25,5 @@ except ImportError:
     pass
 
 setuptools.setup(
-    setup_requires=['pbr>=1.8'],
+    setup_requires=['pbr>=2.0.0'],
     pbr=True)
diff -pruN 2.4.1-2/test-requirements.txt 3.5.0-2/test-requirements.txt
--- 2.4.1-2/test-requirements.txt	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/test-requirements.txt	2017-07-20 14:16:34.000000000 +0000
@@ -1,11 +1,11 @@
 # The order of packages is significant, because pip processes them in the order
 # of appearance. Changing the order has an impact on the overall integration
 # process, which may cause wedges in the gate later.
-hacking<0.11,>=0.10.0
-Babel>=2.3.4 # BSD
-coverage>=3.6 # Apache-2.0
+hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
+Babel!=2.4.0,>=2.3.4 # BSD
+coverage!=4.4,>=4.0 # Apache-2.0
 mock>=2.0 # BSD
-mox3>=0.7.0 # Apache-2.0
-sphinx!=1.3b1,<1.3,>=1.2.1 # BSD
-oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0
+mox3!=0.19.0,>=0.7.0 # Apache-2.0
+sphinx>=1.6.2 # BSD
+openstackdocstheme>=1.11.0 # Apache-2.0
 testscenarios>=0.4 # Apache-2.0/BSD
diff -pruN 2.4.1-2/tools/tox_install.sh 3.5.0-2/tools/tox_install.sh
--- 2.4.1-2/tools/tox_install.sh	1970-01-01 00:00:00.000000000 +0000
+++ 3.5.0-2/tools/tox_install.sh	2017-07-20 14:16:34.000000000 +0000
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+# Client constraint file contains this client version pin that is in conflict
+# with installing the client from source. We should remove the version pin in
+# the constraints file before applying it for from-source installation.
+
+CONSTRAINTS_FILE="$1"
+shift 1
+
+set -e
+
+# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get
+# published to logs.openstack.org for easy debugging.
+localfile="$VIRTUAL_ENV/log/upper-constraints.txt"
+
+if [[ "$CONSTRAINTS_FILE" != http* ]]; then
+    CONSTRAINTS_FILE="file://$CONSTRAINTS_FILE"
+fi
+# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep
+curl "$CONSTRAINTS_FILE" --insecure --progress-bar --output "$localfile"
+
+pip install -c"$localfile" openstack-requirements
+
+# This is the main purpose of the script: Allow local installation of
+# the current repo. It is listed in constraints file and thus any
+# install will be constrained and we need to unconstrain it.
+edit-constraints "$localfile" -- "$CLIENT_NAME"
+
+pip install -c"$localfile" -U "$@"
+exit $?
diff -pruN 2.4.1-2/tox.ini 3.5.0-2/tox.ini
--- 2.4.1-2/tox.ini	2016-09-27 16:40:19.000000000 +0000
+++ 3.5.0-2/tox.ini	2017-07-20 14:16:34.000000000 +0000
@@ -1,12 +1,12 @@
 [tox]
-minversion = 1.6
+minversion = 2.0
 skipsdist = True
-envlist = py27,py27dj18,pep8,py34
+envlist = py27,py27dj18,pep8,py35
 
 [testenv]
 usedevelop = True
-install_command = pip install -U {opts} {packages}
-setenv = VIRTUAL_ENV={envdir}
+install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
+setenv = {[env-common]setenv}
          NOSE_WITH_OPENSTACK=1
          NOSE_OPENSTACK_COLOR=1
          NOSE_OPENSTACK_RED=0.05
@@ -16,8 +16,14 @@ deps = -r{toxinidir}/requirements.txt
        -r{toxinidir}/test-requirements.txt
 commands = python openstack_auth/tests/run_tests.py {posargs}
 
+[env-common]
+setenv = VIRTUAL_ENV={envdir}
+         BRANCH_NAME=master
+         CLIENT_NAME=django_openstack_auth
+
 [testenv:cover]
-setenv = DJANGO_SETTINGS_MODULE=openstack_auth.tests.settings
+setenv = {[env-common]setenv}
+         DJANGO_SETTINGS_MODULE=openstack_auth.tests.settings
 commands =
     python -m coverage erase
     python -m coverage run openstack_auth/tests/run_tests.py  {posargs}
@@ -37,27 +43,22 @@ commands = pip install django>=1.9,<1.10
 commands = pip install django>=1.10,<1.11
            python openstack_auth/tests/run_tests.py {posargs}
 
+[testenv:py27dj111]
+commands = pip install django>=1.11,<2.0
+           python openstack_auth/tests/run_tests.py {posargs}
+
 [testenv:pep8]
-setenv = DJANGO_SETTINGS_MODULE=openstack_auth.tests.settings
+setenv = {[env-common]setenv}
+         DJANGO_SETTINGS_MODULE=openstack_auth.tests.settings
 commands = flake8
 
 [testenv:venv]
 commands = {posargs}
 
 [testenv:docs]
-setenv = DJANGO_SETTINGS_MODULE=openstack_auth.tests.settings
+setenv = {[env-common]setenv}
 commands = python setup.py build_sphinx
 
 [flake8]
 builtins = _
 exclude =  .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,panel_template,dash_template,local_settings.py
-
-[hacking]
-import_exceptions = django.conf.settings,
-                    django.core.urlresolvers.reverse,
-                    django.conf.urls.include,
-                    django.conf.urls.patterns,
-                    django.conf.urls.url,
-                    django.utils.translation.pgettext_lazy,
-                    django.utils.translation.ugettext_lazy,
-                    django.utils.translation.ungettext_lazy,
