Implement HTTP Basic client support in keystoneauth1
A new basic auth plugin is added which enables HTTP Basic authentication for standalone services. Like the noauth plugin, the endpoint needs to be specified explicitly, along with the username and password. An example of a standalone server implementing HTTP Basic can be seen in Ironic change https://review.opendev.org/#/c/727467/ Change-Id: Ib3f0a9c518d031a67f9605cf64a8a9cc81131ed3 Story: 2007656 Task: 39741
This commit is contained in:
parent
646192d7fd
commit
ff68663217
@ -226,6 +226,34 @@ you can continue as well:
|
||||
sess = session.Session(auth=auth)
|
||||
sess.get_token()
|
||||
|
||||
Standalone Plugins
|
||||
------------------
|
||||
|
||||
Services can be deployed in a standalone environment where there is no integration
|
||||
with an identity service. The following plugins are provided to support standalone
|
||||
services:
|
||||
|
||||
- :py:class:`~keystoneauth1.http_basic.HTTPBasicAuth`: HTTP Basic authentication
|
||||
- :py:class:`~keystoneauth1.noauth.NoAuth`: No authentication
|
||||
|
||||
Standalone plugins must be given an `endpoint` that points to the URL of the one
|
||||
service being used, since there is no service catalog to look up endpoints::
|
||||
|
||||
from keystoneauth1 import session
|
||||
from keystoneauth1 import noauth
|
||||
auth = noauth.NoAuth(endpoint='http://hostname:6385/')
|
||||
sess = session.Session(auth=auth)
|
||||
|
||||
:py:class:`~keystoneauth1.http_basic.HTTPBasicAuth` also requres a `username` and
|
||||
`password`::
|
||||
|
||||
from keystoneauth1 import session
|
||||
from keystoneauth1 import http_basic
|
||||
auth = http_basic.HTTPBasicAuth(endpoint='http://hostname:6385/',
|
||||
username='myUser',
|
||||
password='myPassword')
|
||||
sess = session.Session(auth=auth)
|
||||
|
||||
Federation
|
||||
==========
|
||||
|
||||
@ -374,6 +402,8 @@ possible to specify a plugin to load via name. The authentication options that
|
||||
are available are then specific to the plugin that you specified. Currently the
|
||||
authentication plugins that are available in `keystoneauth` are:
|
||||
|
||||
- http_basic: :py:class:`keystoneauth1.http_basic.HTTPBasicAuth`
|
||||
- none: :py:class:`keystoneauth1.noauth.NoAuth`
|
||||
- password: :py:class:`keystoneauth1.identity.generic.Password`
|
||||
- token: :py:class:`keystoneauth1.identity.generic.Token`
|
||||
- v2password: :py:class:`keystoneauth1.identity.v2.Password`
|
||||
|
55
keystoneauth1/http_basic.py
Normal file
55
keystoneauth1/http_basic.py
Normal file
@ -0,0 +1,55 @@
|
||||
# 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 base64
|
||||
|
||||
from keystoneauth1 import plugin
|
||||
|
||||
AUTH_HEADER_NAME = 'Authorization'
|
||||
|
||||
|
||||
class HTTPBasicAuth(plugin.BaseAuthPlugin):
|
||||
"""A provider that will always use HTTP Basic authentication.
|
||||
|
||||
This is useful to unify session/adapter loading for services
|
||||
that might be deployed in standalone mode.
|
||||
"""
|
||||
|
||||
def __init__(self, endpoint=None, username=None, password=None):
|
||||
super(HTTPBasicAuth, self).__init__()
|
||||
self.endpoint = endpoint
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
def get_token(self, session, **kwargs):
|
||||
if self.username is None or self.password is None:
|
||||
return None
|
||||
token = bytes('%s:%s' % (self.username, self.password),
|
||||
encoding='utf-8')
|
||||
encoded = base64.b64encode(token)
|
||||
return str(encoded, encoding='utf-8')
|
||||
|
||||
def get_headers(self, session, **kwargs):
|
||||
token = self.get_token(session)
|
||||
if not token:
|
||||
return None
|
||||
auth = 'Basic %s' % token
|
||||
return {AUTH_HEADER_NAME: auth}
|
||||
|
||||
def get_endpoint(self, session, **kwargs):
|
||||
"""Return the supplied endpoint.
|
||||
|
||||
Using this plugin the same endpoint is returned regardless of the
|
||||
parameters passed to the plugin. endpoint_override overrides the
|
||||
endpoint specified when constructing the plugin.
|
||||
"""
|
||||
return kwargs.get('endpoint_override') or self.endpoint
|
48
keystoneauth1/loading/_plugins/http_basic.py
Normal file
48
keystoneauth1/loading/_plugins/http_basic.py
Normal file
@ -0,0 +1,48 @@
|
||||
# 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 http_basic
|
||||
from keystoneauth1 import loading
|
||||
|
||||
|
||||
class HTTPBasicAuth(loading.BaseLoader):
|
||||
"""Use HTTP Basic authentication to perform requests.
|
||||
|
||||
This can be used to instantiate clients for services deployed in
|
||||
standalone mode.
|
||||
|
||||
There is no fetching a service catalog or determining scope information
|
||||
and so it cannot be used by clients that expect to use this scope
|
||||
information.
|
||||
|
||||
"""
|
||||
|
||||
@property
|
||||
def plugin_class(self):
|
||||
return http_basic.HTTPBasicAuth
|
||||
|
||||
def get_options(self):
|
||||
options = super(HTTPBasicAuth, self).get_options()
|
||||
|
||||
options.extend([
|
||||
loading.Opt('username',
|
||||
help='Username',
|
||||
deprecated=[loading.Opt('user-name')]),
|
||||
loading.Opt('password',
|
||||
secret=True,
|
||||
prompt='Password: ',
|
||||
help="User's password"),
|
||||
loading.Opt('endpoint',
|
||||
help='The endpoint that will always be used'),
|
||||
])
|
||||
|
||||
return options
|
49
keystoneauth1/tests/unit/test_http_basic.py
Normal file
49
keystoneauth1/tests/unit/test_http_basic.py
Normal file
@ -0,0 +1,49 @@
|
||||
# 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 http_basic
|
||||
from keystoneauth1.loading._plugins import http_basic as loader
|
||||
from keystoneauth1 import session
|
||||
from keystoneauth1.tests.unit import utils
|
||||
|
||||
|
||||
class HTTPBasicAuthTest(utils.TestCase):
|
||||
|
||||
TEST_URL = 'http://server/prefix'
|
||||
|
||||
def test_basic_case(self):
|
||||
self.requests_mock.get(self.TEST_URL, text='body')
|
||||
|
||||
a = http_basic.HTTPBasicAuth(username='myName', password='myPassword')
|
||||
s = session.Session(auth=a)
|
||||
|
||||
data = s.get(self.TEST_URL, authenticated=True)
|
||||
|
||||
self.assertEqual(data.text, 'body')
|
||||
self.assertRequestHeaderEqual(
|
||||
'Authorization', 'Basic bXlOYW1lOm15UGFzc3dvcmQ=')
|
||||
self.assertIsNone(a.get_endpoint(s))
|
||||
|
||||
def test_basic_options(self):
|
||||
opts = loader.HTTPBasicAuth().get_options()
|
||||
self.assertEqual(['username', 'password', 'endpoint'],
|
||||
[o.name for o in opts])
|
||||
|
||||
def test_get_endpoint(self):
|
||||
a = http_basic.HTTPBasicAuth(endpoint=self.TEST_URL)
|
||||
s = session.Session(auth=a)
|
||||
self.assertEqual(self.TEST_URL, a.get_endpoint(s))
|
||||
|
||||
def test_get_endpoint_with_override(self):
|
||||
a = http_basic.HTTPBasicAuth(endpoint=self.TEST_URL)
|
||||
s = session.Session(auth=a)
|
||||
self.assertEqual('foo', a.get_endpoint(s, endpoint_override='foo'))
|
7
releasenotes/notes/basic-http-auth-45bea4298209df75.yaml
Normal file
7
releasenotes/notes/basic-http-auth-45bea4298209df75.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
A new ``http_basic`` auth plugin is added which enables HTTP Basic
|
||||
authentication for standalone services. Like the ``noauth`` plugin, the
|
||||
``endpoint`` needs to be specified explicitly, along with the
|
||||
``username`` and ``password``.
|
@ -42,6 +42,7 @@ betamax =
|
||||
|
||||
keystoneauth1.plugin =
|
||||
none = keystoneauth1.loading._plugins.noauth:NoAuth
|
||||
http_basic = keystoneauth1.loading._plugins.http_basic:HTTPBasicAuth
|
||||
password = keystoneauth1.loading._plugins.identity.generic:Password
|
||||
token = keystoneauth1.loading._plugins.identity.generic:Token
|
||||
admin_token = keystoneauth1.loading._plugins.admin_token:AdminToken
|
||||
|
Loading…
x
Reference in New Issue
Block a user