diff --git a/nova/db/api.py b/nova/db/api.py index 1ea427a98bea..b109e1baa7a4 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -2052,16 +2052,22 @@ def console_auth_token_create(context, values): return IMPL.console_auth_token_create(context, values) -def console_auth_token_get_valid(context, token_hash, instance_uuid): +def console_auth_token_get_valid(context, token_hash, instance_uuid=None): """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. + + If instance_uuid is specified, the token is validated against both + expiry and instance_uuid. + + If instance_uuid is not specified, the token is validated against + expiry only. """ return IMPL.console_auth_token_get_valid(context, token_hash, - instance_uuid) + instance_uuid=instance_uuid) def console_auth_token_destroy_all_by_instance(context, instance_uuid): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index ee21559d11f3..a753ef974ead 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1864,7 +1864,7 @@ def instance_destroy(context, instance_uuid, constraint=None): filter_by(instance_uuid=instance_uuid).\ soft_delete() # NOTE(snikitin): We can't use model_query here, because there is no - # column 'deleted' in 'tags' table. + # column 'deleted' in 'tags' or 'console_auth_tokens' tables. context.session.query(models.Tag).filter_by( resource_id=instance_uuid).delete() context.session.query(models.ConsoleAuthToken).filter_by( @@ -6646,13 +6646,15 @@ def console_auth_token_create(context, values): @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() +def console_auth_token_get_valid(context, token_hash, instance_uuid=None): + if instance_uuid is not None: + _check_instance_exists_in_project(context, instance_uuid) + query = context.session.query(models.ConsoleAuthToken).\ + filter_by(token_hash=token_hash) + if instance_uuid is not None: + query = query.filter_by(instance_uuid=instance_uuid) + return query.filter( + models.ConsoleAuthToken.expires > timeutils.utcnow_ts()).first() @pick_context_manager_writer diff --git a/nova/tests/unit/db/test_db_api.py b/nova/tests/unit/db/test_db_api.py index b256451966dd..01136c48b575 100644 --- a/nova/tests/unit/db/test_db_api.py +++ b/nova/tests/unit/db/test_db_api.py @@ -10519,6 +10519,7 @@ class ConsoleAuthTokenTestCase(test.TestCase): 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.assertEqual(hash1, db_obj1['token_hash']) self.assertIsNone(db_obj2, "the token uuid should not match") def test_console_auth_token_destroy_expired_by_host(self): @@ -10551,6 +10552,37 @@ class ConsoleAuthTokenTestCase(test.TestCase): self.assertIsNotNone(db_obj2, "a valid token should be found here") self.assertIsNotNone(db_obj3, "a valid token should be found here") + def test_console_auth_token_get_valid_without_uuid_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) + self.assertIsNotNone(db_obj1, "a valid token should be in database") + + db.instance_destroy(self.context, uuid1) + db_obj1 = db.console_auth_token_get_valid(self.context, hash1) + self.assertIsNone(db_obj1, "the token should have been deleted") + + def test_console_auth_token_get_valid_without_uuid_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) + db_obj2 = db.console_auth_token_get_valid(self.context, hash2) + self.assertIsNone(db_obj1, "the token should have expired") + self.assertIsNotNone(db_obj2, "a valid token should be found here") + class SortMarkerHelper(test.TestCase): def setUp(self):