Add 303 as redirect code for k2k plugin

Some service providers, like mod_mellon return a 303 response
upon successful authentication. The "requests" package only
considers 302, as per the following example::

  >>> import requests
  >>> requests.codes['found']
  302

Change-Id: I5797f490f2e57d1c952e769bc0ef4b96c08f9a83
Related-Bug: 1501918
This commit is contained in:
Rodrigo Duarte 2016-04-25 16:19:48 -03:00
parent 9d51a2ae60
commit 1a2a579393
2 changed files with 28 additions and 9 deletions

View File

@ -10,7 +10,6 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import requests
import six import six
from keystoneauth1 import access from keystoneauth1 import access
@ -42,6 +41,9 @@ class Keystone2Keystone(federation._Rescoped):
"""Path where the ECP wrapped SAML assertion should be presented to the """Path where the ECP wrapped SAML assertion should be presented to the
Keystone Service Provider.""" Keystone Service Provider."""
HTTP_MOVED_TEMPORARILY = 302
HTTP_SEE_OTHER = 303
def __init__(self, base_plugin, service_provider, **kwargs): def __init__(self, base_plugin, service_provider, **kwargs):
super(Keystone2Keystone, self).__init__(auth_url=None, **kwargs) super(Keystone2Keystone, self).__init__(auth_url=None, **kwargs)
@ -147,11 +149,12 @@ class Keystone2Keystone(federation._Rescoped):
authenticated=False, authenticated=False,
redirect=False) redirect=False)
# Don't follow HTTP specs - after the HTTP 302 response don't repeat # Don't follow HTTP specs - after the HTTP 302/303 response don't
# the call directed to the Location URL. In this case, this is an # repeat the call directed to the Location URL. In this case, this is
# indication that SAML2 session is now active and protected resource # an indication that SAML2 session is now active and protected resource
# can be accessed. # can be accessed.
if response.status_code == requests.codes['found']: if response.status_code in (self.HTTP_MOVED_TEMPORARILY,
self.HTTP_SEE_OTHER):
response = session.get( response = session.get(
sp_auth_url, sp_auth_url,
headers={'Content-Type': 'application/vnd.paos+xml'}, headers={'Content-Type': 'application/vnd.paos+xml'},

View File

@ -133,7 +133,7 @@ class K2KAuthPluginTest(utils.TestCase):
username=self.TEST_USER, username=self.TEST_USER,
password=self.TEST_PASS) password=self.TEST_PASS)
def _mock_k2k_flow_urls(self): def _mock_k2k_flow_urls(self, redirect_code=302):
# List versions available for auth # List versions available for auth
self.requests_mock.get( self.requests_mock.get(
self.TEST_URL, self.TEST_URL,
@ -148,13 +148,13 @@ class K2KAuthPluginTest(utils.TestCase):
headers={'Content-Type': 'application/vnd.paos+xml'}, headers={'Content-Type': 'application/vnd.paos+xml'},
status_code=200) status_code=200)
# The SP should respond with a 302 # The SP should respond with a redirect (302 or 303)
self.requests_mock.register_uri( self.requests_mock.register_uri(
'POST', 'POST',
self.SP_URL, self.SP_URL,
content=six.b(k2k_fixtures.TOKEN_BASED_ECP), content=six.b(k2k_fixtures.TOKEN_BASED_ECP),
headers={'Content-Type': 'application/vnd.paos+xml'}, headers={'Content-Type': 'application/vnd.paos+xml'},
status_code=302) status_code=redirect_code)
# Should not follow the redirect URL, but use the auth_url attribute # Should not follow the redirect URL, but use the auth_url attribute
self.requests_mock.register_uri( self.requests_mock.register_uri(
@ -226,12 +226,28 @@ class K2KAuthPluginTest(utils.TestCase):
self.assertEqual(k2k_fixtures.UNSCOPED_TOKEN_HEADER, self.assertEqual(k2k_fixtures.UNSCOPED_TOKEN_HEADER,
response.headers['X-Subject-Token']) response.headers['X-Subject-Token'])
def test_send_ecp_authn_response_303_redirect(self):
self._mock_k2k_flow_urls(redirect_code=303)
# Perform the request
response = self.k2kplugin._send_service_provider_ecp_authn_response(
self.session, self.SP_URL, self.SP_AUTH_URL)
# Check the response
self.assertEqual(k2k_fixtures.UNSCOPED_TOKEN_HEADER,
response.headers['X-Subject-Token'])
def test_end_to_end_workflow(self): def test_end_to_end_workflow(self):
self._mock_k2k_flow_urls() self._mock_k2k_flow_urls()
auth_ref = self.k2kplugin.get_auth_ref(self.session) auth_ref = self.k2kplugin.get_auth_ref(self.session)
self.assertEqual(k2k_fixtures.UNSCOPED_TOKEN_HEADER, self.assertEqual(k2k_fixtures.UNSCOPED_TOKEN_HEADER,
auth_ref.auth_token) auth_ref.auth_token)
def test_end_to_end_workflow_303_redirect(self):
self._mock_k2k_flow_urls(redirect_code=303)
auth_ref = self.k2kplugin.get_auth_ref(self.session)
self.assertEqual(k2k_fixtures.UNSCOPED_TOKEN_HEADER,
auth_ref.auth_token)
def test_end_to_end_with_generic_password(self): def test_end_to_end_with_generic_password(self):
# List versions available for auth # List versions available for auth
self.requests_mock.get( self.requests_mock.get(
@ -247,7 +263,7 @@ class K2KAuthPluginTest(utils.TestCase):
headers={'Content-Type': 'application/vnd.paos+xml'}, headers={'Content-Type': 'application/vnd.paos+xml'},
status_code=200) status_code=200)
# The SP should respond with a 302 # The SP should respond with a redirect (302 or 303)
self.requests_mock.register_uri( self.requests_mock.register_uri(
'POST', 'POST',
self.SP_URL, self.SP_URL,