Add console auth tokens db api methods
Console auth tokens will be saved in the database instead of in memory in a console auth server. Adding the db api methods to create token records, get them and delete all tokens for an instance in this patch. The following patch in the series will add the console auth token object. Change-Id: I881faa62f3be4986b38d11c4ac059672ae45c11f Co-Authored-By: Eli Qiao <qiaoliyong@gmail.com> partially-implements: blueprint convert-consoles-to-objects
This commit is contained in:
parent
c880706ddd
commit
0c5ff5057e
@ -2045,3 +2045,39 @@ def instance_tag_delete_all(context, instance_uuid):
|
||||
def instance_tag_exists(context, instance_uuid, tag):
|
||||
"""Check if specified tag exist on the instance."""
|
||||
return IMPL.instance_tag_exists(context, instance_uuid, tag)
|
||||
|
||||
|
||||
####################
|
||||
|
||||
|
||||
def console_auth_token_create(context, values):
|
||||
"""Create a console authorization."""
|
||||
return IMPL.console_auth_token_create(context, values)
|
||||
|
||||
|
||||
def console_auth_token_get_valid(context, token_hash, instance_uuid):
|
||||
"""Get a valid console authorization by token_hash and instance_uuid.
|
||||
|
||||
The console authorizations expire at the time specified by their
|
||||
'expires' column. An expired console auth token will not be returned
|
||||
to the caller - it is treated as if it does not exist.
|
||||
"""
|
||||
return IMPL.console_auth_token_get_valid(context,
|
||||
token_hash,
|
||||
instance_uuid)
|
||||
|
||||
|
||||
def console_auth_token_destroy_all_by_instance(context, instance_uuid):
|
||||
"""Delete all console authorizations belonging to the instance."""
|
||||
return IMPL.console_auth_token_destroy_all_by_instance(context,
|
||||
instance_uuid)
|
||||
|
||||
|
||||
def console_auth_token_destroy_expired_by_host(context, host):
|
||||
"""Delete expired console authorizations belonging to the host.
|
||||
|
||||
The console authorizations expire at the time specified by their
|
||||
'expires' column. This function is used to garbage collect expired
|
||||
tokens associated with the given host.
|
||||
"""
|
||||
return IMPL.console_auth_token_destroy_expired_by_host(context, host)
|
||||
|
@ -6803,3 +6803,40 @@ def instance_tag_exists(context, instance_uuid, tag):
|
||||
q = context.session.query(models.Tag).filter_by(
|
||||
resource_id=instance_uuid, tag=tag)
|
||||
return context.session.query(q.exists()).scalar()
|
||||
|
||||
|
||||
####################
|
||||
|
||||
|
||||
@pick_context_manager_writer
|
||||
def console_auth_token_create(context, values):
|
||||
instance_uuid = values.get('instance_uuid')
|
||||
_check_instance_exists_in_project(context, instance_uuid)
|
||||
token_ref = models.ConsoleAuthToken()
|
||||
token_ref.update(values)
|
||||
context.session.add(token_ref)
|
||||
return token_ref
|
||||
|
||||
|
||||
@pick_context_manager_reader
|
||||
def console_auth_token_get_valid(context, token_hash, instance_uuid):
|
||||
_check_instance_exists_in_project(context, instance_uuid)
|
||||
return context.session.query(models.ConsoleAuthToken).\
|
||||
filter_by(token_hash=token_hash).\
|
||||
filter_by(instance_uuid=instance_uuid).\
|
||||
filter(models.ConsoleAuthToken.expires > timeutils.utcnow_ts()).\
|
||||
first()
|
||||
|
||||
|
||||
@pick_context_manager_writer
|
||||
def console_auth_token_destroy_all_by_instance(context, instance_uuid):
|
||||
context.session.query(models.ConsoleAuthToken).\
|
||||
filter_by(instance_uuid=instance_uuid).delete()
|
||||
|
||||
|
||||
@pick_context_manager_writer
|
||||
def console_auth_token_destroy_expired_by_host(context, host):
|
||||
context.session.query(models.ConsoleAuthToken).\
|
||||
filter_by(host=host).\
|
||||
filter(models.ConsoleAuthToken.expires <= timeutils.utcnow_ts()).\
|
||||
delete()
|
||||
|
@ -65,6 +65,7 @@ from nova import objects
|
||||
from nova.objects import fields
|
||||
from nova import quota
|
||||
from nova import test
|
||||
from nova.tests.unit import fake_console_auth_token
|
||||
from nova.tests.unit import matchers
|
||||
from nova.tests import uuidsentinel
|
||||
from nova import utils
|
||||
@ -9981,3 +9982,135 @@ class TestInstanceTagsFiltering(test.TestCase):
|
||||
'not-tags': [u't5', u't6'],
|
||||
'not-tags-any': [u't7', u't8']})
|
||||
self._assertEqualInstanceUUIDs([uuids[3], uuids[5], uuids[6]], result)
|
||||
|
||||
|
||||
class ConsoleAuthTokenTestCase(test.TestCase):
|
||||
|
||||
def _create_instances(self, uuids):
|
||||
for uuid in uuids:
|
||||
db.instance_create(self.context,
|
||||
{'uuid': uuid,
|
||||
'project_id': self.context.project_id})
|
||||
|
||||
def _create(self, token_hash, instance_uuid, expire_offset, host=None):
|
||||
t = copy.deepcopy(fake_console_auth_token.fake_token_dict)
|
||||
del t['id']
|
||||
t['token_hash'] = token_hash
|
||||
t['instance_uuid'] = instance_uuid
|
||||
t['expires'] = timeutils.utcnow_ts() + expire_offset
|
||||
if host:
|
||||
t['host'] = host
|
||||
db.console_auth_token_create(self.context, t)
|
||||
|
||||
def setUp(self):
|
||||
super(ConsoleAuthTokenTestCase, self).setUp()
|
||||
self.context = context.RequestContext('fake', 'fake')
|
||||
|
||||
def test_console_auth_token_create_no_instance(self):
|
||||
t = copy.deepcopy(fake_console_auth_token.fake_token_dict)
|
||||
del t['id']
|
||||
self.assertRaises(exception.InstanceNotFound,
|
||||
db.console_auth_token_create,
|
||||
self.context, t)
|
||||
|
||||
def test_console_auth_token_get_valid_deleted_instance(self):
|
||||
uuid1 = uuidsentinel.uuid1
|
||||
hash1 = utils.get_sha256_str(uuidsentinel.token1)
|
||||
self._create_instances([uuid1])
|
||||
self._create(hash1, uuid1, 100)
|
||||
|
||||
db_obj1 = db.console_auth_token_get_valid(self.context, hash1, uuid1)
|
||||
self.assertIsNotNone(db_obj1, "a valid token should be in database")
|
||||
|
||||
db.instance_destroy(self.context, uuid1)
|
||||
self.assertRaises(exception.InstanceNotFound,
|
||||
db.console_auth_token_get_valid,
|
||||
self.context, hash1, uuid1)
|
||||
|
||||
def test_console_auth_token_destroy_all_by_instance(self):
|
||||
uuid1 = uuidsentinel.uuid1
|
||||
uuid2 = uuidsentinel.uuid2
|
||||
hash1 = utils.get_sha256_str(uuidsentinel.token1)
|
||||
hash2 = utils.get_sha256_str(uuidsentinel.token2)
|
||||
hash3 = utils.get_sha256_str(uuidsentinel.token3)
|
||||
self._create_instances([uuid1, uuid2])
|
||||
self._create(hash1, uuid1, 100)
|
||||
self._create(hash2, uuid1, 100)
|
||||
self._create(hash3, uuid2, 100)
|
||||
|
||||
db_obj1 = db.console_auth_token_get_valid(self.context, hash1, uuid1)
|
||||
db_obj2 = db.console_auth_token_get_valid(self.context, hash2, uuid1)
|
||||
db_obj3 = db.console_auth_token_get_valid(self.context, hash3, uuid2)
|
||||
self.assertIsNotNone(db_obj1, "a valid token should be in database")
|
||||
self.assertIsNotNone(db_obj2, "a valid token should be in database")
|
||||
self.assertIsNotNone(db_obj3, "a valid token should be in database")
|
||||
|
||||
db.console_auth_token_destroy_all_by_instance(self.context, uuid1)
|
||||
|
||||
db_obj4 = db.console_auth_token_get_valid(self.context, hash1, uuid1)
|
||||
db_obj5 = db.console_auth_token_get_valid(self.context, hash2, uuid1)
|
||||
db_obj6 = db.console_auth_token_get_valid(self.context, hash3, uuid2)
|
||||
self.assertIsNone(db_obj4, "no valid token should be in database")
|
||||
self.assertIsNone(db_obj5, "no valid token should be in database")
|
||||
self.assertIsNotNone(db_obj6, "a valid token should be in database")
|
||||
|
||||
def test_console_auth_token_get_valid_by_expiry(self):
|
||||
uuid1 = uuidsentinel.uuid1
|
||||
uuid2 = uuidsentinel.uuid2
|
||||
hash1 = utils.get_sha256_str(uuidsentinel.token1)
|
||||
hash2 = utils.get_sha256_str(uuidsentinel.token2)
|
||||
self.addCleanup(timeutils.clear_time_override)
|
||||
timeutils.set_time_override(timeutils.utcnow())
|
||||
self._create_instances([uuid1, uuid2])
|
||||
|
||||
self._create(hash1, uuid1, 10)
|
||||
timeutils.advance_time_seconds(100)
|
||||
self._create(hash2, uuid2, 10)
|
||||
|
||||
db_obj1 = db.console_auth_token_get_valid(self.context, hash1, uuid1)
|
||||
db_obj2 = db.console_auth_token_get_valid(self.context, hash2, uuid2)
|
||||
self.assertIsNone(db_obj1, "the token should have expired")
|
||||
self.assertIsNotNone(db_obj2, "a valid token should be found here")
|
||||
|
||||
def test_console_auth_token_get_valid_by_uuid(self):
|
||||
uuid1 = uuidsentinel.uuid1
|
||||
uuid2 = uuidsentinel.uuid2
|
||||
hash1 = utils.get_sha256_str(uuidsentinel.token1)
|
||||
self._create_instances([uuid1, uuid2])
|
||||
|
||||
self._create(hash1, uuid1, 10)
|
||||
|
||||
db_obj1 = db.console_auth_token_get_valid(self.context, hash1, uuid1)
|
||||
db_obj2 = db.console_auth_token_get_valid(self.context, hash1, uuid2)
|
||||
self.assertIsNotNone(db_obj1, "a valid token should be found here")
|
||||
self.assertIsNone(db_obj2, "the token uuid should not match")
|
||||
|
||||
def test_console_auth_token_destroy_expired_by_host(self):
|
||||
uuid1 = uuidsentinel.uuid1
|
||||
uuid2 = uuidsentinel.uuid2
|
||||
uuid3 = uuidsentinel.uuid3
|
||||
hash1 = utils.get_sha256_str(uuidsentinel.token1)
|
||||
hash2 = utils.get_sha256_str(uuidsentinel.token2)
|
||||
hash3 = utils.get_sha256_str(uuidsentinel.token3)
|
||||
self.addCleanup(timeutils.clear_time_override)
|
||||
timeutils.set_time_override(timeutils.utcnow())
|
||||
self._create_instances([uuid1, uuid2, uuid3])
|
||||
|
||||
self._create(hash1, uuid1, 10)
|
||||
self._create(hash2, uuid2, 10, host='other-host')
|
||||
timeutils.advance_time_seconds(100)
|
||||
self._create(hash3, uuid3, 10)
|
||||
|
||||
db.console_auth_token_destroy_expired_by_host(
|
||||
self.context, 'fake-host')
|
||||
|
||||
# the api only supports getting unexpired tokens
|
||||
# but by rolling back time we can see if a token that
|
||||
# should be deleted is still there
|
||||
timeutils.advance_time_seconds(-100)
|
||||
db_obj1 = db.console_auth_token_get_valid(self.context, hash1, uuid1)
|
||||
db_obj2 = db.console_auth_token_get_valid(self.context, hash2, uuid2)
|
||||
db_obj3 = db.console_auth_token_get_valid(self.context, hash3, uuid3)
|
||||
self.assertIsNone(db_obj1, "the token should have been deleted")
|
||||
self.assertIsNotNone(db_obj2, "a valid token should be found here")
|
||||
self.assertIsNotNone(db_obj3, "a valid token should be found here")
|
||||
|
33
nova/tests/unit/fake_console_auth_token.py
Normal file
33
nova/tests/unit/fake_console_auth_token.py
Normal file
@ -0,0 +1,33 @@
|
||||
# Copyright 2016 Intel Corp.
|
||||
# Copyright 2016 Hewlett Packard Enterprise Development Company LP
|
||||
#
|
||||
# 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 nova.tests import uuidsentinel
|
||||
from nova import utils
|
||||
|
||||
fake_token = uuidsentinel.token
|
||||
fake_token_hash = utils.get_sha256_str(fake_token)
|
||||
fake_instance_uuid = uuidsentinel.instance
|
||||
fake_token_dict = {
|
||||
'created_at': None,
|
||||
'updated_at': None,
|
||||
'id': 123,
|
||||
'token_hash': fake_token_hash,
|
||||
'console_type': 'fake-type',
|
||||
'host': 'fake-host',
|
||||
'port': 1000,
|
||||
'internal_access_path': 'fake-path',
|
||||
'instance_uuid': fake_instance_uuid,
|
||||
'expires': 100,
|
||||
}
|
@ -1211,6 +1211,19 @@ def get_hash_str(base_str):
|
||||
return hashlib.md5(base_str).hexdigest()
|
||||
|
||||
|
||||
def get_sha256_str(base_str):
|
||||
"""Returns string that represents sha256 hash of base_str (in hex format).
|
||||
|
||||
sha1 and md5 are known to be breakable, so sha256 is a better option
|
||||
when the hash is being used for security purposes. If hashing passwords
|
||||
or anything else that needs to be retained for a long period a salted
|
||||
hash is better.
|
||||
"""
|
||||
if isinstance(base_str, six.text_type):
|
||||
base_str = base_str.encode('utf-8')
|
||||
return hashlib.sha256(base_str).hexdigest()
|
||||
|
||||
|
||||
def filter_and_format_resource_metadata(resource_type, resource_list,
|
||||
search_filts, metadata_type=None):
|
||||
"""Get all metadata for a list of resources after filtering.
|
||||
|
Loading…
x
Reference in New Issue
Block a user