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
|
||||
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.
|
||||
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
|
||||
requests>=2.14.2 # 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 flask
|
||||
from ironic_lib import auth_basic
|
||||
from werkzeug import exceptions as wz_exc
|
||||
|
||||
from sushy_tools.emulator import memoize
|
||||
@ -38,15 +39,54 @@ from sushy_tools import error
|
||||
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):
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, extra_config=None):
|
||||
super().__init__(__name__)
|
||||
# Turn off strict_slashes on all routes
|
||||
self.url_map.strict_slashes = False
|
||||
config_file = os.environ.get('SUSHY_EMULATOR_CONFIG')
|
||||
if 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
|
||||
@memoize.memoize()
|
||||
|
@ -9,6 +9,8 @@
|
||||
# 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 tempfile
|
||||
from unittest import mock
|
||||
|
||||
from oslotest import base
|
||||
@ -51,6 +53,37 @@ class CommonTestCase(EmulatorTestCase):
|
||||
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):
|
||||
|
||||
@patch_resource('chassis')
|
||||
|
Loading…
x
Reference in New Issue
Block a user