190 lines
7.4 KiB
Python
190 lines
7.4 KiB
Python
# 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 urllib.parse
|
|
import uuid
|
|
|
|
from oslo_config import fixture as config
|
|
|
|
import testtools
|
|
|
|
from keystoneclient.auth import conf
|
|
from keystoneclient.contrib.auth.v3 import oidc
|
|
from keystoneclient import session
|
|
from keystoneclient.tests.unit.v3 import utils
|
|
|
|
|
|
ACCESS_TOKEN_ENDPOINT_RESP = {"access_token": "z5H1ITZLlJVDHQXqJun",
|
|
"token_type": "bearer",
|
|
"expires_in": 3599,
|
|
"scope": "profile",
|
|
"refresh_token": "DCERsh83IAhu9bhavrp"}
|
|
|
|
KEYSTONE_TOKEN_VALUE = uuid.uuid4().hex
|
|
UNSCOPED_TOKEN = {
|
|
"token": {
|
|
"issued_at": "2014-06-09T09:48:59.643406Z",
|
|
"extras": {},
|
|
"methods": ["oidc"],
|
|
"expires_at": "2014-06-09T10:48:59.643375Z",
|
|
"user": {
|
|
"OS-FEDERATION": {
|
|
"identity_provider": {
|
|
"id": "bluepages"
|
|
},
|
|
"protocol": {
|
|
"id": "oidc"
|
|
},
|
|
"groups": [
|
|
{"id": "1764fa5cf69a49a4918131de5ce4af9a"}
|
|
]
|
|
},
|
|
"id": "oidc_user%40example.com",
|
|
"name": "oidc_user@example.com"
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class AuthenticateOIDCTests(utils.TestCase):
|
|
|
|
GROUP = 'auth'
|
|
|
|
def setUp(self):
|
|
super(AuthenticateOIDCTests, self).setUp()
|
|
|
|
self.deprecations.expect_deprecations()
|
|
|
|
self.conf_fixture = self.useFixture(config.Config())
|
|
conf.register_conf_options(self.conf_fixture.conf, group=self.GROUP)
|
|
|
|
self.session = session.Session()
|
|
|
|
self.IDENTITY_PROVIDER = 'bluepages'
|
|
self.PROTOCOL = 'oidc'
|
|
self.USER_NAME = 'oidc_user@example.com'
|
|
self.PASSWORD = uuid.uuid4().hex
|
|
self.CLIENT_ID = uuid.uuid4().hex
|
|
self.CLIENT_SECRET = uuid.uuid4().hex
|
|
self.ACCESS_TOKEN_ENDPOINT = 'https://localhost:8020/oidc/token'
|
|
self.FEDERATION_AUTH_URL = '%s/%s' % (
|
|
self.TEST_URL,
|
|
'OS-FEDERATION/identity_providers/bluepages/protocols/oidc/auth')
|
|
|
|
self.oidcplugin = oidc.OidcPassword(
|
|
self.TEST_URL,
|
|
self.IDENTITY_PROVIDER,
|
|
self.PROTOCOL,
|
|
username=self.USER_NAME,
|
|
password=self.PASSWORD,
|
|
client_id=self.CLIENT_ID,
|
|
client_secret=self.CLIENT_SECRET,
|
|
access_token_endpoint=self.ACCESS_TOKEN_ENDPOINT)
|
|
|
|
@testtools.skip("TypeError: __init__() got an unexpected keyword"
|
|
" argument 'project_name'")
|
|
def test_conf_params(self):
|
|
"""Ensure OpenID Connect config options work."""
|
|
section = uuid.uuid4().hex
|
|
identity_provider = uuid.uuid4().hex
|
|
protocol = uuid.uuid4().hex
|
|
username = uuid.uuid4().hex
|
|
password = uuid.uuid4().hex
|
|
client_id = uuid.uuid4().hex
|
|
client_secret = uuid.uuid4().hex
|
|
access_token_endpoint = uuid.uuid4().hex
|
|
|
|
self.conf_fixture.config(auth_section=section, group=self.GROUP)
|
|
conf.register_conf_options(self.conf_fixture.conf, group=self.GROUP)
|
|
|
|
self.conf_fixture.register_opts(oidc.OidcPassword.get_options(),
|
|
group=section)
|
|
self.conf_fixture.config(auth_plugin='v3oidcpassword',
|
|
identity_provider=identity_provider,
|
|
protocol=protocol,
|
|
username=username,
|
|
password=password,
|
|
client_id=client_id,
|
|
client_secret=client_secret,
|
|
access_token_endpoint=access_token_endpoint,
|
|
group=section)
|
|
|
|
a = conf.load_from_conf_options(self.conf_fixture.conf, self.GROUP)
|
|
self.assertEqual(identity_provider, a.identity_provider)
|
|
self.assertEqual(protocol, a.protocol)
|
|
self.assertEqual(username, a.username)
|
|
self.assertEqual(password, a.password)
|
|
self.assertEqual(client_id, a.client_id)
|
|
self.assertEqual(client_secret, a.client_secret)
|
|
self.assertEqual(access_token_endpoint, a.access_token_endpoint)
|
|
|
|
def test_initial_call_to_get_access_token(self):
|
|
"""Test initial call, expect JSON access token."""
|
|
# Mock the output that creates the access token
|
|
self.requests_mock.post(
|
|
self.ACCESS_TOKEN_ENDPOINT,
|
|
json=ACCESS_TOKEN_ENDPOINT_RESP)
|
|
|
|
# Prep all the values and send the request
|
|
grant_type = 'password'
|
|
scope = 'profile email'
|
|
client_auth = (self.CLIENT_ID, self.CLIENT_SECRET)
|
|
payload = {'grant_type': grant_type, 'username': self.USER_NAME,
|
|
'password': self.PASSWORD, 'scope': scope}
|
|
res = self.oidcplugin._get_access_token(self.session,
|
|
client_auth,
|
|
payload,
|
|
self.ACCESS_TOKEN_ENDPOINT)
|
|
|
|
# Verify the request matches the expected structure
|
|
self.assertEqual(self.ACCESS_TOKEN_ENDPOINT, res.request.url)
|
|
self.assertEqual('POST', res.request.method)
|
|
encoded_payload = urllib.parse.urlencode(payload)
|
|
self.assertEqual(encoded_payload, res.request.body)
|
|
|
|
def test_second_call_to_protected_url(self):
|
|
"""Test subsequent call, expect Keystone token."""
|
|
# Mock the output that creates the keystone token
|
|
self.requests_mock.post(
|
|
self.FEDERATION_AUTH_URL,
|
|
json=UNSCOPED_TOKEN,
|
|
headers={'X-Subject-Token': KEYSTONE_TOKEN_VALUE})
|
|
|
|
# Prep all the values and send the request
|
|
access_token = uuid.uuid4().hex
|
|
headers = {'Authorization': 'Bearer ' + access_token}
|
|
res = self.oidcplugin._get_keystone_token(self.session,
|
|
headers,
|
|
self.FEDERATION_AUTH_URL)
|
|
|
|
# Verify the request matches the expected structure
|
|
self.assertEqual(self.FEDERATION_AUTH_URL, res.request.url)
|
|
self.assertEqual('POST', res.request.method)
|
|
self.assertEqual(headers['Authorization'],
|
|
res.request.headers['Authorization'])
|
|
|
|
def test_end_to_end_workflow(self):
|
|
"""Test full OpenID Connect workflow."""
|
|
# Mock the output that creates the access token
|
|
self.requests_mock.post(
|
|
self.ACCESS_TOKEN_ENDPOINT,
|
|
json=ACCESS_TOKEN_ENDPOINT_RESP)
|
|
|
|
# Mock the output that creates the keystone token
|
|
self.requests_mock.post(
|
|
self.FEDERATION_AUTH_URL,
|
|
json=UNSCOPED_TOKEN,
|
|
headers={'X-Subject-Token': KEYSTONE_TOKEN_VALUE})
|
|
|
|
response = self.oidcplugin.get_unscoped_auth_ref(self.session)
|
|
self.assertEqual(KEYSTONE_TOKEN_VALUE, response.auth_token)
|