add openid connect plugins
Refactor and add the existing password based plugin that exists in keystoneclient: https://github.com/openstack/python-keystoneclient/blob/master/keystoneclient/contrib/auth/v3/oidc.py It's been refactored so that a base class is created with arguments that are common to several openid connect flows. The patch also includes support for a few auth flow called authorization code. Co-Authored-By: Zhao Jian <jianzj@cn.ibm.com> Change-Id: I35db68e288e174617a05c0db3d77d5a86048fe9e
This commit is contained in:
parent
6ce09e133d
commit
b3a454c56c
@ -14,6 +14,7 @@ from keystoneauth1.identity import base
|
||||
from keystoneauth1.identity import generic
|
||||
from keystoneauth1.identity import v2
|
||||
from keystoneauth1.identity import v3
|
||||
from keystoneauth1.identity.v3 import oidc
|
||||
|
||||
|
||||
BaseIdentityPlugin = base.BaseIdentityPlugin
|
||||
@ -27,6 +28,8 @@ V3Token = v3.Token
|
||||
Password = generic.Password
|
||||
Token = generic.Token
|
||||
|
||||
V3OidcPassword = oidc.OidcPassword
|
||||
V3OidcAuthorizationCode = oidc.OidcAuthorizationCode
|
||||
|
||||
__all__ = ['BaseIdentityPlugin',
|
||||
'Password',
|
||||
@ -34,4 +37,6 @@ __all__ = ['BaseIdentityPlugin',
|
||||
'V2Password',
|
||||
'V2Token',
|
||||
'V3Password',
|
||||
'V3Token']
|
||||
'V3Token',
|
||||
'V3OidcPassword',
|
||||
'V3OidcAuthorizationCode']
|
||||
|
@ -15,6 +15,7 @@ from keystoneauth1.identity.v3.federation import * # noqa
|
||||
from keystoneauth1.identity.v3.k2k import * # noqa
|
||||
from keystoneauth1.identity.v3.password import * # noqa
|
||||
from keystoneauth1.identity.v3.token import * # noqa
|
||||
from keystoneauth1.identity.v3.oidc import * # noqa
|
||||
|
||||
|
||||
__all__ = ['Auth',
|
||||
@ -30,4 +31,7 @@ __all__ = ['Auth',
|
||||
'PasswordMethod',
|
||||
|
||||
'Token',
|
||||
'TokenMethod']
|
||||
'TokenMethod'
|
||||
|
||||
'OidcAuthorizationCode',
|
||||
'OidcPassword']
|
||||
|
266
keystoneauth1/identity/v3/oidc.py
Normal file
266
keystoneauth1/identity/v3/oidc.py
Normal file
@ -0,0 +1,266 @@
|
||||
# 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.
|
||||
|
||||
from keystoneauth1 import _utils
|
||||
from keystoneauth1 import access
|
||||
from keystoneauth1.identity.v3 import federation
|
||||
|
||||
__all__ = ['OidcAuthorizationCode',
|
||||
'OidcPassword']
|
||||
|
||||
|
||||
class _OidcBase(federation.FederationBaseAuth):
|
||||
"""Base class for different OpenID Connect based flows
|
||||
|
||||
The OpenID Connect specification can be found at::
|
||||
``http://openid.net/specs/openid-connect-core-1_0.html``
|
||||
"""
|
||||
|
||||
def __init__(self, auth_url, identity_provider, protocol,
|
||||
client_id, client_secret, access_token_endpoint,
|
||||
grant_type, access_token_type, **kwargs):
|
||||
"""The OpenID Connect plugin expects the following:
|
||||
|
||||
:param auth_url: URL of the Identity Service
|
||||
:type auth_url: string
|
||||
|
||||
:param identity_provider: Name of the Identity Provider the client
|
||||
will authenticate against
|
||||
:type identity_provider: string
|
||||
|
||||
:param protocol: Protocol name as configured in keystone
|
||||
:type protocol: string
|
||||
|
||||
:param client_id: OAuth 2.0 Client ID
|
||||
:type client_id: string
|
||||
|
||||
:param client_secret: OAuth 2.0 Client Secret
|
||||
:type client_secret: string
|
||||
|
||||
:param access_token_endpoint: OpenID Connect Provider Token Endpoint,
|
||||
for example:
|
||||
https://localhost:8020/oidc/OP/token
|
||||
:type access_token_endpoint: string
|
||||
|
||||
:param grant_type: OpenID Connect grant type, it represents the flow
|
||||
that is used to talk to the OP. Valid values are:
|
||||
"authorization_code", "refresh_token", or
|
||||
"password".
|
||||
:type grant_type: string
|
||||
|
||||
:param access_token_type: OAuth 2.0 Authorization Server Introspection
|
||||
token type, it is used to decide which type
|
||||
of token will be used when processing token
|
||||
introspection. Valid values are:
|
||||
"access_token" or "id_token"
|
||||
:type access_token_type: string
|
||||
|
||||
"""
|
||||
super(_OidcBase, self).__init__(auth_url, identity_provider, protocol,
|
||||
**kwargs)
|
||||
self.client_id = client_id
|
||||
self.client_secret = client_secret
|
||||
self.access_token_endpoint = access_token_endpoint
|
||||
self.grant_type = grant_type
|
||||
self.access_token_type = access_token_type
|
||||
|
||||
def _get_access_token(self, session, client_auth, payload,
|
||||
access_token_endpoint):
|
||||
"""Exchange a variety of user supplied values for an access token.
|
||||
|
||||
:param session: a session object to send out HTTP requests.
|
||||
:type session: keystoneauth.session.Session
|
||||
|
||||
:param client_auth: a tuple representing client id and secret
|
||||
:type client_auth: tuple
|
||||
|
||||
:param payload: a dict containing various OpenID Connect values, for
|
||||
example::
|
||||
{'grant_type': 'password', 'username': self.username,
|
||||
'password': self.password, 'scope': self.scope}
|
||||
:type payload: dict
|
||||
|
||||
:param access_token_endpoint: URL to use to get an access token, for
|
||||
example: https://localhost/oidc/token
|
||||
:type access_token_endpoint: string
|
||||
"""
|
||||
op_response = session.post(self.access_token_endpoint,
|
||||
requests_auth=client_auth,
|
||||
data=payload,
|
||||
authenticated=False)
|
||||
return op_response
|
||||
|
||||
def _get_keystone_token(self, session, headers, federated_token_url):
|
||||
"""Exchange an acess token for a keystone token.
|
||||
|
||||
By Sending the access token in an `Authorization: Bearer` header, to
|
||||
an OpenID Connect protected endpoint (Federated Token URL). The
|
||||
OpenID Connect server will use the access token to look up information
|
||||
about the authenticated user (this technique is called instrospection).
|
||||
The output of the instrospection will be an OpenID Connect Claim, that
|
||||
will be used against the mapping engine. Should the mapping engine
|
||||
succeed, a Keystone token will be presented to the user.
|
||||
|
||||
:param session: a session object to send out HTTP requests.
|
||||
:type session: keystoneauth.session.Session
|
||||
|
||||
:param headers: an Authorization header containing the access token.
|
||||
:type headers_: dict
|
||||
|
||||
:param federated_auth_url: Protected URL for federated authentication,
|
||||
for example: https://localhost:5000/v3/\
|
||||
OS-FEDERATION/identity_providers/bluepages/\
|
||||
protocols/oidc/auth
|
||||
:type federated_auth_url: string
|
||||
"""
|
||||
auth_response = session.post(self.federated_token_url,
|
||||
headers=headers,
|
||||
authenticated=False)
|
||||
return auth_response
|
||||
|
||||
|
||||
class OidcPassword(_OidcBase):
|
||||
"""Implementation for OpenID Connect Resource Owner Password Credential"""
|
||||
|
||||
@_utils.positional(4)
|
||||
def __init__(self, auth_url, identity_provider, protocol,
|
||||
client_id, client_secret, access_token_endpoint,
|
||||
grant_type='password', access_token_type='access_token',
|
||||
username=None, password=None, scope='profile'):
|
||||
"""The OpenID Password plugin expects the following:
|
||||
:param username: Username used to authenticate
|
||||
:type username: string
|
||||
|
||||
:param password: Password used to authenticate
|
||||
:type password: string
|
||||
|
||||
:param scope: OpenID Connect scope that is requested from OP,
|
||||
defaults to "profile", for example: "profile email"
|
||||
:type scope: string
|
||||
|
||||
"""
|
||||
super(OidcPassword, self).__init__(
|
||||
auth_url=auth_url,
|
||||
identity_provider=identity_provider,
|
||||
protocol=protocol,
|
||||
client_id=client_id,
|
||||
client_secret=client_secret,
|
||||
access_token_endpoint=access_token_endpoint,
|
||||
grant_type=grant_type,
|
||||
access_token_type=access_token_type)
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.scope = scope
|
||||
|
||||
def get_unscoped_auth_ref(self, session):
|
||||
"""Authenticate with OpenID Connect and get back claims.
|
||||
|
||||
This is a multi-step process. First an access token must be retrieved,
|
||||
to do this, the username and password, the OpenID Connect client ID
|
||||
and secret, and the access token endpoint must be known.
|
||||
|
||||
Secondly, we then exchange the access token upon accessing the
|
||||
protected Keystone endpoint (federated auth URL). This will trigger
|
||||
the OpenID Connect Provider to perform a user introspection and
|
||||
retrieve information (specified in the scope) about the user in
|
||||
the form of an OpenID Connect Claim. These claims will be sent
|
||||
to Keystone in the form of environment variables.
|
||||
|
||||
:param session: a session object to send out HTTP requests.
|
||||
:type session: keystoneauth1.session.Session
|
||||
|
||||
:returns: a token data representation
|
||||
:rtype: :py:class:`keystoneauth1.access.AccessInfoV3`
|
||||
"""
|
||||
|
||||
# get an access token
|
||||
client_auth = (self.client_id, self.client_secret)
|
||||
payload = {'grant_type': self.grant_type, 'username': self.username,
|
||||
'password': self.password, 'scope': self.scope}
|
||||
response = self._get_access_token(session, client_auth, payload,
|
||||
self.access_token_endpoint)
|
||||
access_token = response.json()[self.access_token_type]
|
||||
|
||||
# use access token against protected URL
|
||||
headers = {'Authorization': 'Bearer ' + access_token}
|
||||
response = self._get_keystone_token(session, headers,
|
||||
self.federated_token_url)
|
||||
|
||||
# grab the unscoped token
|
||||
return access.create(resp=response)
|
||||
|
||||
|
||||
class OidcAuthorizationCode(_OidcBase):
|
||||
"""Implementation for OpenID Connect Authorization Code"""
|
||||
|
||||
@_utils.positional(4)
|
||||
def __init__(self, auth_url, identity_provider, protocol,
|
||||
client_id, client_secret, access_token_endpoint,
|
||||
grant_type='authorization_code',
|
||||
access_token_type='access_token',
|
||||
redirect_uri=None, code=None):
|
||||
"""The OpenID Authorization Code plugin expects the following:
|
||||
:param redirect_uri: OpenID Connect Client Redirect URL
|
||||
:type redirect_uri: string
|
||||
|
||||
:param code: OAuth 2.0 Authorization Code
|
||||
:type code: string
|
||||
|
||||
"""
|
||||
super(OidcAuthorizationCode, self).__init__(
|
||||
auth_url=auth_url,
|
||||
identity_provider=identity_provider,
|
||||
protocol=protocol,
|
||||
client_id=client_id,
|
||||
client_secret=client_secret,
|
||||
access_token_endpoint=access_token_endpoint,
|
||||
grant_type=grant_type,
|
||||
access_token_type=access_token_type)
|
||||
self.redirect_uri = redirect_uri
|
||||
self.code = code
|
||||
|
||||
def get_unscoped_auth_ref(self, session):
|
||||
"""Authenticate with OpenID Connect and get back claims.
|
||||
|
||||
This is a multi-step process. First an access token must be retrieved,
|
||||
to do this, an authorization code and redirect URL must be given.
|
||||
|
||||
Secondly, we then exchange the access token upon accessing the
|
||||
protected Keystone endpoint (federated auth URL). This will trigger
|
||||
the OpenID Connect Provider to perform a user introspection and
|
||||
retrieve information (specified in the scope) about the user in
|
||||
the form of an OpenID Connect Claim. These claims will be sent
|
||||
to Keystone in the form of environment variables.
|
||||
|
||||
:param session: a session object to send out HTTP requests.
|
||||
:type session: keystoneauth1.session.Session
|
||||
|
||||
:returns: a token data representation
|
||||
:rtype: :py:class:`keystoneauth1.access.AccessInfoV3`
|
||||
"""
|
||||
|
||||
# get an access token
|
||||
client_auth = (self.client_id, self.client_secret)
|
||||
payload = {'grant_type': self.grant_type,
|
||||
'redirect_uri': self.redirect_uri,
|
||||
'code': self.code}
|
||||
response = self._get_access_token(session, client_auth, payload,
|
||||
self.access_token_endpoint)
|
||||
access_token = response.json()[self.access_token_type]
|
||||
|
||||
# use access token against protected URL
|
||||
headers = {'Authorization': 'Bearer ' + access_token}
|
||||
response = self._get_keystone_token(session, headers,
|
||||
self.federated_token_url)
|
||||
|
||||
# grab the unscoped token
|
||||
return access.create(resp=response)
|
@ -113,3 +113,61 @@ class FederatedBase(BaseV3Loader):
|
||||
])
|
||||
|
||||
return options
|
||||
|
||||
|
||||
class _OpenIDConnectBase(FederatedBase):
|
||||
|
||||
def get_options(self):
|
||||
options = super(_OpenIDConnectBase, self).get_options()
|
||||
|
||||
options.extend([
|
||||
loading.Opt('client-id', help='OAuth 2.0 Client ID'),
|
||||
loading.Opt('client-secret', help='OAuth 2.0 Client Secret'),
|
||||
loading.Opt('access-token-endpoint',
|
||||
help='OpenID Connect Provider Token Endpoint'),
|
||||
loading.Opt('access-token-type',
|
||||
help='OAuth 2.0 Authorization Server Introspection '
|
||||
'token type, it is used to decide which type '
|
||||
'of token will be used when processing token '
|
||||
'introspection. Valid values are: '
|
||||
'"access_token" or "id_token"'),
|
||||
])
|
||||
|
||||
return options
|
||||
|
||||
|
||||
class OpenIDConnectPassword(_OpenIDConnectBase):
|
||||
|
||||
@property
|
||||
def plugin_class(self):
|
||||
return identity.V3OidcPassword
|
||||
|
||||
def get_options(self):
|
||||
options = super(OpenIDConnectPassword, self).get_options()
|
||||
|
||||
options.extend([
|
||||
loading.Opt('username', help='Username'),
|
||||
loading.Opt('password', help='Password'),
|
||||
loading.Opt('openid-scope', default="profile",
|
||||
help='OpenID Connect scope that is requested from OP')
|
||||
])
|
||||
|
||||
return options
|
||||
|
||||
|
||||
class OpenIDConnectAuthorizationCode(_OpenIDConnectBase):
|
||||
|
||||
@property
|
||||
def plugin_class(self):
|
||||
return identity.V3OidcAuthorizationCode
|
||||
|
||||
def get_options(self):
|
||||
options = super(OpenIDConnectAuthorizationCode, self).get_options()
|
||||
|
||||
options.extend([
|
||||
loading.Opt('redirect-uri', help='OpenID Connect Redirect URL'),
|
||||
loading.Opt('authorization-code',
|
||||
help='OAuth 2.0 Authorization Code'),
|
||||
])
|
||||
|
||||
return options
|
||||
|
159
keystoneauth1/tests/unit/identity/test_identity_v3_oidc.py
Normal file
159
keystoneauth1/tests/unit/identity/test_identity_v3_oidc.py
Normal file
@ -0,0 +1,159 @@
|
||||
# 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 uuid
|
||||
|
||||
from six.moves import urllib
|
||||
|
||||
from keystoneauth1.identity.v3 import oidc
|
||||
from keystoneauth1 import session
|
||||
from keystoneauth1.tests.unit import oidc_fixtures
|
||||
from keystoneauth1.tests.unit import utils
|
||||
|
||||
|
||||
KEYSTONE_TOKEN_VALUE = uuid.uuid4().hex
|
||||
|
||||
|
||||
class AuthenticateOIDCTests(utils.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(AuthenticateOIDCTests, self).setUp()
|
||||
self.session = session.Session()
|
||||
|
||||
self.AUTH_URL = 'http://keystone:5000/v3'
|
||||
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.AUTH_URL,
|
||||
'OS-FEDERATION/identity_providers/bluepages/protocols/oidc/auth')
|
||||
self.REDIRECT_URL = 'urn:ietf:wg:oauth:2.0:oob'
|
||||
self.CODE = '4/M9TNz2G9WVwYxSjx0w9AgA1bOmryJltQvOhQMq0czJs.cnLNVAfqwG'
|
||||
|
||||
self.oidc_password = oidc.OidcPassword(
|
||||
self.AUTH_URL,
|
||||
self.IDENTITY_PROVIDER,
|
||||
self.PROTOCOL,
|
||||
client_id=self.CLIENT_ID,
|
||||
client_secret=self.CLIENT_SECRET,
|
||||
access_token_endpoint=self.ACCESS_TOKEN_ENDPOINT,
|
||||
username=self.USER_NAME,
|
||||
password=self.PASSWORD)
|
||||
|
||||
self.oidc_grant = oidc.OidcAuthorizationCode(
|
||||
self.AUTH_URL,
|
||||
self.IDENTITY_PROVIDER,
|
||||
self.PROTOCOL,
|
||||
client_id=self.CLIENT_ID,
|
||||
client_secret=self.CLIENT_SECRET,
|
||||
access_token_endpoint=self.ACCESS_TOKEN_ENDPOINT,
|
||||
redirect_uri=self.REDIRECT_URL,
|
||||
code=self.CODE)
|
||||
|
||||
|
||||
class OIDCPasswordTests(AuthenticateOIDCTests):
|
||||
|
||||
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=oidc_fixtures.ACCESS_TOKEN_VIA_PASSWORD_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.oidc_password._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=oidc_fixtures.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.oidc_password._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=oidc_fixtures.ACCESS_TOKEN_VIA_PASSWORD_RESP)
|
||||
|
||||
# Mock the output that creates the keystone token
|
||||
self.requests_mock.post(
|
||||
self.FEDERATION_AUTH_URL,
|
||||
json=oidc_fixtures.UNSCOPED_TOKEN,
|
||||
headers={'X-Subject-Token': KEYSTONE_TOKEN_VALUE})
|
||||
|
||||
response = self.oidc_password.get_unscoped_auth_ref(self.session)
|
||||
self.assertEqual(KEYSTONE_TOKEN_VALUE, response.auth_token)
|
||||
|
||||
|
||||
class OIDCAuthorizationGrantTests(AuthenticateOIDCTests):
|
||||
|
||||
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=oidc_fixtures.ACCESS_TOKEN_VIA_AUTH_GRANT_RESP)
|
||||
|
||||
# Prep all the values and send the request
|
||||
grant_type = 'authorization_code'
|
||||
client_auth = (self.CLIENT_ID, self.CLIENT_SECRET)
|
||||
payload = {'grant_type': grant_type,
|
||||
'redirect_uri': self.REDIRECT_URL,
|
||||
'code': self.CODE}
|
||||
res = self.oidc_grant._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)
|
51
keystoneauth1/tests/unit/oidc_fixtures.py
Normal file
51
keystoneauth1/tests/unit/oidc_fixtures.py
Normal file
@ -0,0 +1,51 @@
|
||||
# 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.
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ACCESS_TOKEN_VIA_PASSWORD_RESP = {
|
||||
"access_token": "z5H1ITZLlJVDHQXqJun",
|
||||
"token_type": "bearer",
|
||||
"expires_in": 3599,
|
||||
"scope": "profile",
|
||||
"refresh_token": "DCERsh83IAhu9bhavrp"
|
||||
}
|
||||
|
||||
ACCESS_TOKEN_VIA_AUTH_GRANT_RESP = {
|
||||
"access_token": "ya29.jgGIjfVrBPWLStWSU3eh8ioE6hG06QQ",
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 3600,
|
||||
"refresh_token": "1/ySXNO9XISBMIgOrJDtdun6zK6XiATCKT",
|
||||
"id_token": "eyJhbGciOiJSUzI1Ni8hOYHuZT8dt_yynmJVhcU"
|
||||
}
|
@ -33,6 +33,8 @@ keystoneauth1.plugin =
|
||||
v2token = keystoneauth1.loading._plugins.identity.v2:Token
|
||||
v3password = keystoneauth1.loading._plugins.identity.v3:Password
|
||||
v3token = keystoneauth1.loading._plugins.identity.v3:Token
|
||||
v3oidcpassword = keystoneauth1.loading._plugins.identity.v3:OpenIDConnectPassword
|
||||
v3oidcauthcode = keystoneauth1.loading._plugins.identity.v3:OpenIDConnectAuthorizationCode
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
|
Loading…
Reference in New Issue
Block a user