Support HTTP basic auth
Change-Id: I58d5e43c33f5ebce4fc7f120917839d8dd28c56b
This commit is contained in:
parent
00fcd2f4f7
commit
7998eeca08
@ -13,6 +13,9 @@ SUSHY_EMULATOR_SSL_CERT = None
|
|||||||
# If SSL certificate is being served, this is its RSA private key
|
# If SSL certificate is being served, this is its RSA private key
|
||||||
SUSHY_EMULATOR_SSL_KEY = None
|
SUSHY_EMULATOR_SSL_KEY = None
|
||||||
|
|
||||||
|
# If authentication is desired, set this to an htpasswd file.
|
||||||
|
SUSHY_EMULATOR_AUTH_FILE = None
|
||||||
|
|
||||||
# The OpenStack cloud ID to use. This option enables OpenStack driver.
|
# The OpenStack cloud ID to use. This option enables OpenStack driver.
|
||||||
SUSHY_EMULATOR_OS_CLOUD = None
|
SUSHY_EMULATOR_OS_CLOUD = None
|
||||||
|
|
||||||
|
5
releasenotes/notes/auth-044dab149ab0c03f.yaml
Normal file
5
releasenotes/notes/auth-044dab149ab0c03f.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Supports HTTP basic authentication of Redfish endpoints. Set the new
|
||||||
|
``SUSHY_EMULATOR_AUTH_FILE`` variable to the path of an htpasswd file.
|
@ -6,3 +6,4 @@ pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
|||||||
Flask>=1.0.2 # BSD
|
Flask>=1.0.2 # BSD
|
||||||
requests>=2.14.2 # Apache-2.0
|
requests>=2.14.2 # Apache-2.0
|
||||||
tenacity>=6.2.0 # Apache-2.0
|
tenacity>=6.2.0 # Apache-2.0
|
||||||
|
ironic-lib>=4.6.1 # Apache-2.0
|
||||||
|
@ -22,6 +22,7 @@ import ssl
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
|
from ironic_lib import auth_basic
|
||||||
from werkzeug import exceptions as wz_exc
|
from werkzeug import exceptions as wz_exc
|
||||||
|
|
||||||
from sushy_tools.emulator import memoize
|
from sushy_tools.emulator import memoize
|
||||||
@ -38,15 +39,54 @@ from sushy_tools import error
|
|||||||
from sushy_tools.error import FishyError
|
from sushy_tools.error import FishyError
|
||||||
|
|
||||||
|
|
||||||
|
def _render_error(message):
|
||||||
|
return {
|
||||||
|
"error": {
|
||||||
|
"code": "Base.1.0.GeneralError",
|
||||||
|
"message": message,
|
||||||
|
"@Message.ExtendedInfo": [
|
||||||
|
{
|
||||||
|
"@odata.type": ("/redfish/v1/$metadata"
|
||||||
|
"#Message.1.0.0.Message"),
|
||||||
|
"MessageId": "Base.1.0.GeneralError"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class RedfishAuthMiddleware(auth_basic.BasicAuthMiddleware):
|
||||||
|
|
||||||
|
_EXCLUDE_PATHS = frozenset(['', 'redfish', 'redfish/v1'])
|
||||||
|
|
||||||
|
def __call__(self, env, start_response):
|
||||||
|
path = env.get('PATH_INFO', '')
|
||||||
|
if path.strip('/') in self._EXCLUDE_PATHS:
|
||||||
|
return self.app(env, start_response)
|
||||||
|
else:
|
||||||
|
return super().__call__(env, start_response)
|
||||||
|
|
||||||
|
def format_exception(self, e):
|
||||||
|
response = super().format_exception(e)
|
||||||
|
response.json_body = _render_error(str(e))
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
class Application(flask.Flask):
|
class Application(flask.Flask):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, extra_config=None):
|
||||||
super().__init__(__name__)
|
super().__init__(__name__)
|
||||||
# Turn off strict_slashes on all routes
|
# Turn off strict_slashes on all routes
|
||||||
self.url_map.strict_slashes = False
|
self.url_map.strict_slashes = False
|
||||||
config_file = os.environ.get('SUSHY_EMULATOR_CONFIG')
|
config_file = os.environ.get('SUSHY_EMULATOR_CONFIG')
|
||||||
if config_file:
|
if config_file:
|
||||||
self.config.from_pyfile(config_file)
|
self.config.from_pyfile(config_file)
|
||||||
|
if extra_config:
|
||||||
|
self.config.update(extra_config)
|
||||||
|
|
||||||
|
auth_file = self.config.get("SUSHY_EMULATOR_AUTH_FILE")
|
||||||
|
if auth_file:
|
||||||
|
self.wsgi_app = RedfishAuthMiddleware(self.wsgi_app, auth_file)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@memoize.memoize()
|
@memoize.memoize()
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# 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 tempfile
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from oslotest import base
|
from oslotest import base
|
||||||
@ -51,6 +53,37 @@ class CommonTestCase(EmulatorTestCase):
|
|||||||
self.assertEqual('RedvirtService', response.json['Id'])
|
self.assertEqual('RedvirtService', response.json['Id'])
|
||||||
|
|
||||||
|
|
||||||
|
TEST_PASSWD = \
|
||||||
|
b"admin:$2y$05$mYl8KMwM94l4LR/sw1teIeA6P2u8gfX16e8wvT7NmGgAM5r9jgLl."
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticatedTestCase(base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.auth_file = tempfile.NamedTemporaryFile()
|
||||||
|
self.auth_file.write(TEST_PASSWD)
|
||||||
|
self.auth_file.flush()
|
||||||
|
self.addCleanup(self.auth_file.close)
|
||||||
|
app = main.Application({
|
||||||
|
'SUSHY_EMULATOR_AUTH_FILE': self.auth_file.name})
|
||||||
|
self.app = app.test_client()
|
||||||
|
|
||||||
|
def test_root_resource(self):
|
||||||
|
response = self.app.get('/redfish/v1/')
|
||||||
|
# 404 because this application does not have any routes
|
||||||
|
self.assertEqual(404, response.status_code, response.data)
|
||||||
|
|
||||||
|
def test_authenticated_resource(self):
|
||||||
|
response = self.app.get('/redfish/v1/Systems/',
|
||||||
|
auth=('admin', 'password'))
|
||||||
|
self.assertEqual(404, response.status_code, response.data)
|
||||||
|
|
||||||
|
def test_authentication_failed(self):
|
||||||
|
response = self.app.get('/redfish/v1/Systems/')
|
||||||
|
self.assertEqual(401, response.status_code, response.data)
|
||||||
|
|
||||||
|
|
||||||
class ChassisTestCase(EmulatorTestCase):
|
class ChassisTestCase(EmulatorTestCase):
|
||||||
|
|
||||||
@patch_resource('chassis')
|
@patch_resource('chassis')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user